linux/drivers/cpufreq/qcom-cpufreq-nvmem.c
<<
>>
Prefs
   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
  34enum _msm_id {
  35        MSM8996V3 = 0xF6ul,
  36        APQ8096V3 = 0x123ul,
  37        MSM8996SG = 0x131ul,
  38        APQ8096SG = 0x138ul,
  39};
  40
  41enum _msm8996_version {
  42        MSM8996_V3,
  43        MSM8996_SG,
  44        NUM_OF_MSM8996_VERSIONS,
  45};
  46
  47struct qcom_cpufreq_drv;
  48
  49struct qcom_cpufreq_match_data {
  50        int (*get_version)(struct device *cpu_dev,
  51                           struct nvmem_cell *speedbin_nvmem,
  52                           char **pvs_name,
  53                           struct qcom_cpufreq_drv *drv);
  54        const char **genpd_names;
  55};
  56
  57struct qcom_cpufreq_drv {
  58        struct opp_table **names_opp_tables;
  59        struct opp_table **hw_opp_tables;
  60        struct opp_table **genpd_opp_tables;
  61        u32 versions;
  62        const struct qcom_cpufreq_match_data *data;
  63};
  64
  65static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev;
  66
  67static void get_krait_bin_format_a(struct device *cpu_dev,
  68                                          int *speed, int *pvs, int *pvs_ver,
  69                                          struct nvmem_cell *pvs_nvmem, u8 *buf)
  70{
  71        u32 pte_efuse;
  72
  73        pte_efuse = *((u32 *)buf);
  74
  75        *speed = pte_efuse & 0xf;
  76        if (*speed == 0xf)
  77                *speed = (pte_efuse >> 4) & 0xf;
  78
  79        if (*speed == 0xf) {
  80                *speed = 0;
  81                dev_warn(cpu_dev, "Speed bin: Defaulting to %d\n", *speed);
  82        } else {
  83                dev_dbg(cpu_dev, "Speed bin: %d\n", *speed);
  84        }
  85
  86        *pvs = (pte_efuse >> 10) & 0x7;
  87        if (*pvs == 0x7)
  88                *pvs = (pte_efuse >> 13) & 0x7;
  89
  90        if (*pvs == 0x7) {
  91                *pvs = 0;
  92                dev_warn(cpu_dev, "PVS bin: Defaulting to %d\n", *pvs);
  93        } else {
  94                dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs);
  95        }
  96}
  97
  98static void get_krait_bin_format_b(struct device *cpu_dev,
  99                                          int *speed, int *pvs, int *pvs_ver,
 100                                          struct nvmem_cell *pvs_nvmem, u8 *buf)
 101{
 102        u32 pte_efuse, redundant_sel;
 103
 104        pte_efuse = *((u32 *)buf);
 105        redundant_sel = (pte_efuse >> 24) & 0x7;
 106
 107        *pvs_ver = (pte_efuse >> 4) & 0x3;
 108
 109        switch (redundant_sel) {
 110        case 1:
 111                *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
 112                *speed = (pte_efuse >> 27) & 0xf;
 113                break;
 114        case 2:
 115                *pvs = (pte_efuse >> 27) & 0xf;
 116                *speed = pte_efuse & 0x7;
 117                break;
 118        default:
 119                /* 4 bits of PVS are in efuse register bits 31, 8-6. */
 120                *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
 121                *speed = pte_efuse & 0x7;
 122        }
 123
 124        /* Check SPEED_BIN_BLOW_STATUS */
 125        if (pte_efuse & BIT(3)) {
 126                dev_dbg(cpu_dev, "Speed bin: %d\n", *speed);
 127        } else {
 128                dev_warn(cpu_dev, "Speed bin not set. Defaulting to 0!\n");
 129                *speed = 0;
 130        }
 131
 132        /* Check PVS_BLOW_STATUS */
 133        pte_efuse = *(((u32 *)buf) + 4);
 134        pte_efuse &= BIT(21);
 135        if (pte_efuse) {
 136                dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs);
 137        } else {
 138                dev_warn(cpu_dev, "PVS bin not set. Defaulting to 0!\n");
 139                *pvs = 0;
 140        }
 141
 142        dev_dbg(cpu_dev, "PVS version: %d\n", *pvs_ver);
 143}
 144
 145static enum _msm8996_version qcom_cpufreq_get_msm_id(void)
 146{
 147        size_t len;
 148        u32 *msm_id;
 149        enum _msm8996_version version;
 150
 151        msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
 152        if (IS_ERR(msm_id))
 153                return NUM_OF_MSM8996_VERSIONS;
 154
 155        /* The first 4 bytes are format, next to them is the actual msm-id */
 156        msm_id++;
 157
 158        switch ((enum _msm_id)*msm_id) {
 159        case MSM8996V3:
 160        case APQ8096V3:
 161                version = MSM8996_V3;
 162                break;
 163        case MSM8996SG:
 164        case APQ8096SG:
 165                version = MSM8996_SG;
 166                break;
 167        default:
 168                version = NUM_OF_MSM8996_VERSIONS;
 169        }
 170
 171        return version;
 172}
 173
 174static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
 175                                          struct nvmem_cell *speedbin_nvmem,
 176                                          char **pvs_name,
 177                                          struct qcom_cpufreq_drv *drv)
 178{
 179        size_t len;
 180        u8 *speedbin;
 181        enum _msm8996_version msm8996_version;
 182        *pvs_name = NULL;
 183
 184        msm8996_version = qcom_cpufreq_get_msm_id();
 185        if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
 186                dev_err(cpu_dev, "Not Snapdragon 820/821!");
 187                return -ENODEV;
 188        }
 189
 190        speedbin = nvmem_cell_read(speedbin_nvmem, &len);
 191        if (IS_ERR(speedbin))
 192                return PTR_ERR(speedbin);
 193
 194        switch (msm8996_version) {
 195        case MSM8996_V3:
 196                drv->versions = 1 << (unsigned int)(*speedbin);
 197                break;
 198        case MSM8996_SG:
 199                drv->versions = 1 << ((unsigned int)(*speedbin) + 4);
 200                break;
 201        default:
 202                BUG();
 203                break;
 204        }
 205
 206        kfree(speedbin);
 207        return 0;
 208}
 209
 210static int qcom_cpufreq_krait_name_version(struct device *cpu_dev,
 211                                           struct nvmem_cell *speedbin_nvmem,
 212                                           char **pvs_name,
 213                                           struct qcom_cpufreq_drv *drv)
 214{
 215        int speed = 0, pvs = 0, pvs_ver = 0;
 216        u8 *speedbin;
 217        size_t len;
 218
 219        speedbin = nvmem_cell_read(speedbin_nvmem, &len);
 220
 221        if (IS_ERR(speedbin))
 222                return PTR_ERR(speedbin);
 223
 224        switch (len) {
 225        case 4:
 226                get_krait_bin_format_a(cpu_dev, &speed, &pvs, &pvs_ver,
 227                                       speedbin_nvmem, speedbin);
 228                break;
 229        case 8:
 230                get_krait_bin_format_b(cpu_dev, &speed, &pvs, &pvs_ver,
 231                                       speedbin_nvmem, speedbin);
 232                break;
 233        default:
 234                dev_err(cpu_dev, "Unable to read nvmem data. Defaulting to 0!\n");
 235                return -ENODEV;
 236        }
 237
 238        snprintf(*pvs_name, sizeof("speedXX-pvsXX-vXX"), "speed%d-pvs%d-v%d",
 239                 speed, pvs, pvs_ver);
 240
 241        drv->versions = (1 << speed);
 242
 243        kfree(speedbin);
 244        return 0;
 245}
 246
 247static const struct qcom_cpufreq_match_data match_data_kryo = {
 248        .get_version = qcom_cpufreq_kryo_name_version,
 249};
 250
 251static const struct qcom_cpufreq_match_data match_data_krait = {
 252        .get_version = qcom_cpufreq_krait_name_version,
 253};
 254
 255static const char *qcs404_genpd_names[] = { "cpr", NULL };
 256
 257static const struct qcom_cpufreq_match_data match_data_qcs404 = {
 258        .genpd_names = qcs404_genpd_names,
 259};
 260
 261static int qcom_cpufreq_probe(struct platform_device *pdev)
 262{
 263        struct qcom_cpufreq_drv *drv;
 264        struct nvmem_cell *speedbin_nvmem;
 265        struct device_node *np;
 266        struct device *cpu_dev;
 267        char *pvs_name = "speedXX-pvsXX-vXX";
 268        unsigned cpu;
 269        const struct of_device_id *match;
 270        int ret;
 271
 272        cpu_dev = get_cpu_device(0);
 273        if (!cpu_dev)
 274                return -ENODEV;
 275
 276        np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
 277        if (!np)
 278                return -ENOENT;
 279
 280        ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
 281        if (!ret) {
 282                of_node_put(np);
 283                return -ENOENT;
 284        }
 285
 286        drv = kzalloc(sizeof(*drv), GFP_KERNEL);
 287        if (!drv)
 288                return -ENOMEM;
 289
 290        match = pdev->dev.platform_data;
 291        drv->data = match->data;
 292        if (!drv->data) {
 293                ret = -ENODEV;
 294                goto free_drv;
 295        }
 296
 297        if (drv->data->get_version) {
 298                speedbin_nvmem = of_nvmem_cell_get(np, NULL);
 299                if (IS_ERR(speedbin_nvmem)) {
 300                        if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)
 301                                dev_err(cpu_dev,
 302                                        "Could not get nvmem cell: %ld\n",
 303                                        PTR_ERR(speedbin_nvmem));
 304                        ret = PTR_ERR(speedbin_nvmem);
 305                        goto free_drv;
 306                }
 307
 308                ret = drv->data->get_version(cpu_dev,
 309                                                        speedbin_nvmem, &pvs_name, drv);
 310                if (ret) {
 311                        nvmem_cell_put(speedbin_nvmem);
 312                        goto free_drv;
 313                }
 314                nvmem_cell_put(speedbin_nvmem);
 315        }
 316        of_node_put(np);
 317
 318        drv->names_opp_tables = kcalloc(num_possible_cpus(),
 319                                  sizeof(*drv->names_opp_tables),
 320                                  GFP_KERNEL);
 321        if (!drv->names_opp_tables) {
 322                ret = -ENOMEM;
 323                goto free_drv;
 324        }
 325        drv->hw_opp_tables = kcalloc(num_possible_cpus(),
 326                                  sizeof(*drv->hw_opp_tables),
 327                                  GFP_KERNEL);
 328        if (!drv->hw_opp_tables) {
 329                ret = -ENOMEM;
 330                goto free_opp_names;
 331        }
 332
 333        drv->genpd_opp_tables = kcalloc(num_possible_cpus(),
 334                                        sizeof(*drv->genpd_opp_tables),
 335                                        GFP_KERNEL);
 336        if (!drv->genpd_opp_tables) {
 337                ret = -ENOMEM;
 338                goto free_opp;
 339        }
 340
 341        for_each_possible_cpu(cpu) {
 342                cpu_dev = get_cpu_device(cpu);
 343                if (NULL == cpu_dev) {
 344                        ret = -ENODEV;
 345                        goto free_genpd_opp;
 346                }
 347
 348                if (drv->data->get_version) {
 349
 350                        if (pvs_name) {
 351                                drv->names_opp_tables[cpu] = dev_pm_opp_set_prop_name(
 352                                                                     cpu_dev,
 353                                                                     pvs_name);
 354                                if (IS_ERR(drv->names_opp_tables[cpu])) {
 355                                        ret = PTR_ERR(drv->names_opp_tables[cpu]);
 356                                        dev_err(cpu_dev, "Failed to add OPP name %s\n",
 357                                                pvs_name);
 358                                        goto free_opp;
 359                                }
 360                        }
 361
 362                        drv->hw_opp_tables[cpu] = dev_pm_opp_set_supported_hw(
 363                                                                         cpu_dev, &drv->versions, 1);
 364                        if (IS_ERR(drv->hw_opp_tables[cpu])) {
 365                                ret = PTR_ERR(drv->hw_opp_tables[cpu]);
 366                                dev_err(cpu_dev,
 367                                        "Failed to set supported hardware\n");
 368                                goto free_genpd_opp;
 369                        }
 370                }
 371
 372                if (drv->data->genpd_names) {
 373                        drv->genpd_opp_tables[cpu] =
 374                                dev_pm_opp_attach_genpd(cpu_dev,
 375                                                        drv->data->genpd_names,
 376                                                        NULL);
 377                        if (IS_ERR(drv->genpd_opp_tables[cpu])) {
 378                                ret = PTR_ERR(drv->genpd_opp_tables[cpu]);
 379                                if (ret != -EPROBE_DEFER)
 380                                        dev_err(cpu_dev,
 381                                                "Could not attach to pm_domain: %d\n",
 382                                                ret);
 383                                goto free_genpd_opp;
 384                        }
 385                }
 386        }
 387
 388        cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
 389                                                          NULL, 0);
 390        if (!IS_ERR(cpufreq_dt_pdev)) {
 391                platform_set_drvdata(pdev, drv);
 392                return 0;
 393        }
 394
 395        ret = PTR_ERR(cpufreq_dt_pdev);
 396        dev_err(cpu_dev, "Failed to register platform device\n");
 397
 398free_genpd_opp:
 399        for_each_possible_cpu(cpu) {
 400                if (IS_ERR(drv->genpd_opp_tables[cpu]))
 401                        break;
 402                dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
 403        }
 404        kfree(drv->genpd_opp_tables);
 405free_opp:
 406        for_each_possible_cpu(cpu) {
 407                if (IS_ERR(drv->names_opp_tables[cpu]))
 408                        break;
 409                dev_pm_opp_put_prop_name(drv->names_opp_tables[cpu]);
 410        }
 411        for_each_possible_cpu(cpu) {
 412                if (IS_ERR(drv->hw_opp_tables[cpu]))
 413                        break;
 414                dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
 415        }
 416        kfree(drv->hw_opp_tables);
 417free_opp_names:
 418        kfree(drv->names_opp_tables);
 419free_drv:
 420        kfree(drv);
 421
 422        return ret;
 423}
 424
 425static int qcom_cpufreq_remove(struct platform_device *pdev)
 426{
 427        struct qcom_cpufreq_drv *drv = platform_get_drvdata(pdev);
 428        unsigned int cpu;
 429
 430        platform_device_unregister(cpufreq_dt_pdev);
 431
 432        for_each_possible_cpu(cpu) {
 433                dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]);
 434                dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
 435                dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
 436        }
 437
 438        kfree(drv->names_opp_tables);
 439        kfree(drv->hw_opp_tables);
 440        kfree(drv->genpd_opp_tables);
 441        kfree(drv);
 442
 443        return 0;
 444}
 445
 446static struct platform_driver qcom_cpufreq_driver = {
 447        .probe = qcom_cpufreq_probe,
 448        .remove = qcom_cpufreq_remove,
 449        .driver = {
 450                .name = "qcom-cpufreq-nvmem",
 451        },
 452};
 453
 454static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
 455        { .compatible = "qcom,apq8096", .data = &match_data_kryo },
 456        { .compatible = "qcom,msm8996", .data = &match_data_kryo },
 457        { .compatible = "qcom,qcs404", .data = &match_data_qcs404 },
 458        { .compatible = "qcom,ipq8064", .data = &match_data_krait },
 459        { .compatible = "qcom,apq8064", .data = &match_data_krait },
 460        { .compatible = "qcom,msm8974", .data = &match_data_krait },
 461        { .compatible = "qcom,msm8960", .data = &match_data_krait },
 462        {},
 463};
 464MODULE_DEVICE_TABLE(of, qcom_cpufreq_match_list);
 465
 466/*
 467 * Since the driver depends on smem and nvmem drivers, which may
 468 * return EPROBE_DEFER, all the real activity is done in the probe,
 469 * which may be defered as well. The init here is only registering
 470 * the driver and the platform device.
 471 */
 472static int __init qcom_cpufreq_init(void)
 473{
 474        struct device_node *np = of_find_node_by_path("/");
 475        const struct of_device_id *match;
 476        int ret;
 477
 478        if (!np)
 479                return -ENODEV;
 480
 481        match = of_match_node(qcom_cpufreq_match_list, np);
 482        of_node_put(np);
 483        if (!match)
 484                return -ENODEV;
 485
 486        ret = platform_driver_register(&qcom_cpufreq_driver);
 487        if (unlikely(ret < 0))
 488                return ret;
 489
 490        cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem",
 491                                                     -1, match, sizeof(*match));
 492        ret = PTR_ERR_OR_ZERO(cpufreq_pdev);
 493        if (0 == ret)
 494                return 0;
 495
 496        platform_driver_unregister(&qcom_cpufreq_driver);
 497        return ret;
 498}
 499module_init(qcom_cpufreq_init);
 500
 501static void __exit qcom_cpufreq_exit(void)
 502{
 503        platform_device_unregister(cpufreq_pdev);
 504        platform_driver_unregister(&qcom_cpufreq_driver);
 505}
 506module_exit(qcom_cpufreq_exit);
 507
 508MODULE_DESCRIPTION("Qualcomm Technologies, Inc. CPUfreq driver");
 509MODULE_LICENSE("GPL v2");
 510