linux/drivers/cpufreq/qcom-cpufreq-kryo.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-kryo 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/platform_device.h>
  26#include <linux/pm_opp.h>
  27#include <linux/slab.h>
  28#include <linux/soc/qcom/smem.h>
  29
  30#define MSM_ID_SMEM     137
  31
  32enum _msm_id {
  33        MSM8996V3 = 0xF6ul,
  34        APQ8096V3 = 0x123ul,
  35        MSM8996SG = 0x131ul,
  36        APQ8096SG = 0x138ul,
  37};
  38
  39enum _msm8996_version {
  40        MSM8996_V3,
  41        MSM8996_SG,
  42        NUM_OF_MSM8996_VERSIONS,
  43};
  44
  45struct platform_device *cpufreq_dt_pdev, *kryo_cpufreq_pdev;
  46
  47static enum _msm8996_version qcom_cpufreq_kryo_get_msm_id(void)
  48{
  49        size_t len;
  50        u32 *msm_id;
  51        enum _msm8996_version version;
  52
  53        msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
  54        if (IS_ERR(msm_id))
  55                return NUM_OF_MSM8996_VERSIONS;
  56
  57        /* The first 4 bytes are format, next to them is the actual msm-id */
  58        msm_id++;
  59
  60        switch ((enum _msm_id)*msm_id) {
  61        case MSM8996V3:
  62        case APQ8096V3:
  63                version = MSM8996_V3;
  64                break;
  65        case MSM8996SG:
  66        case APQ8096SG:
  67                version = MSM8996_SG;
  68                break;
  69        default:
  70                version = NUM_OF_MSM8996_VERSIONS;
  71        }
  72
  73        return version;
  74}
  75
  76static int qcom_cpufreq_kryo_probe(struct platform_device *pdev)
  77{
  78        struct opp_table *opp_tables[NR_CPUS] = {0};
  79        enum _msm8996_version msm8996_version;
  80        struct nvmem_cell *speedbin_nvmem;
  81        struct device_node *np;
  82        struct device *cpu_dev;
  83        unsigned cpu;
  84        u8 *speedbin;
  85        u32 versions;
  86        size_t len;
  87        int ret;
  88
  89        cpu_dev = get_cpu_device(0);
  90        if (!cpu_dev)
  91                return -ENODEV;
  92
  93        msm8996_version = qcom_cpufreq_kryo_get_msm_id();
  94        if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
  95                dev_err(cpu_dev, "Not Snapdragon 820/821!");
  96                return -ENODEV;
  97        }
  98
  99        np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
 100        if (!np)
 101                return -ENOENT;
 102
 103        ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
 104        if (!ret) {
 105                of_node_put(np);
 106                return -ENOENT;
 107        }
 108
 109        speedbin_nvmem = of_nvmem_cell_get(np, NULL);
 110        of_node_put(np);
 111        if (IS_ERR(speedbin_nvmem)) {
 112                if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)
 113                        dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
 114                                PTR_ERR(speedbin_nvmem));
 115                return PTR_ERR(speedbin_nvmem);
 116        }
 117
 118        speedbin = nvmem_cell_read(speedbin_nvmem, &len);
 119        nvmem_cell_put(speedbin_nvmem);
 120        if (IS_ERR(speedbin))
 121                return PTR_ERR(speedbin);
 122
 123        switch (msm8996_version) {
 124        case MSM8996_V3:
 125                versions = 1 << (unsigned int)(*speedbin);
 126                break;
 127        case MSM8996_SG:
 128                versions = 1 << ((unsigned int)(*speedbin) + 4);
 129                break;
 130        default:
 131                BUG();
 132                break;
 133        }
 134        kfree(speedbin);
 135
 136        for_each_possible_cpu(cpu) {
 137                cpu_dev = get_cpu_device(cpu);
 138                if (NULL == cpu_dev) {
 139                        ret = -ENODEV;
 140                        goto free_opp;
 141                }
 142
 143                opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
 144                                                              &versions, 1);
 145                if (IS_ERR(opp_tables[cpu])) {
 146                        ret = PTR_ERR(opp_tables[cpu]);
 147                        dev_err(cpu_dev, "Failed to set supported hardware\n");
 148                        goto free_opp;
 149                }
 150        }
 151
 152        cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
 153                                                          NULL, 0);
 154        if (!IS_ERR(cpufreq_dt_pdev))
 155                return 0;
 156
 157        ret = PTR_ERR(cpufreq_dt_pdev);
 158        dev_err(cpu_dev, "Failed to register platform device\n");
 159
 160free_opp:
 161        for_each_possible_cpu(cpu) {
 162                if (IS_ERR_OR_NULL(opp_tables[cpu]))
 163                        break;
 164                dev_pm_opp_put_supported_hw(opp_tables[cpu]);
 165        }
 166
 167        return ret;
 168}
 169
 170static int qcom_cpufreq_kryo_remove(struct platform_device *pdev)
 171{
 172        platform_device_unregister(cpufreq_dt_pdev);
 173        return 0;
 174}
 175
 176static struct platform_driver qcom_cpufreq_kryo_driver = {
 177        .probe = qcom_cpufreq_kryo_probe,
 178        .remove = qcom_cpufreq_kryo_remove,
 179        .driver = {
 180                .name = "qcom-cpufreq-kryo",
 181        },
 182};
 183
 184static const struct of_device_id qcom_cpufreq_kryo_match_list[] __initconst = {
 185        { .compatible = "qcom,apq8096", },
 186        { .compatible = "qcom,msm8996", },
 187        {}
 188};
 189
 190/*
 191 * Since the driver depends on smem and nvmem drivers, which may
 192 * return EPROBE_DEFER, all the real activity is done in the probe,
 193 * which may be defered as well. The init here is only registering
 194 * the driver and the platform device.
 195 */
 196static int __init qcom_cpufreq_kryo_init(void)
 197{
 198        struct device_node *np = of_find_node_by_path("/");
 199        const struct of_device_id *match;
 200        int ret;
 201
 202        if (!np)
 203                return -ENODEV;
 204
 205        match = of_match_node(qcom_cpufreq_kryo_match_list, np);
 206        of_node_put(np);
 207        if (!match)
 208                return -ENODEV;
 209
 210        ret = platform_driver_register(&qcom_cpufreq_kryo_driver);
 211        if (unlikely(ret < 0))
 212                return ret;
 213
 214        kryo_cpufreq_pdev = platform_device_register_simple(
 215                "qcom-cpufreq-kryo", -1, NULL, 0);
 216        ret = PTR_ERR_OR_ZERO(kryo_cpufreq_pdev);
 217        if (0 == ret)
 218                return 0;
 219
 220        platform_driver_unregister(&qcom_cpufreq_kryo_driver);
 221        return ret;
 222}
 223module_init(qcom_cpufreq_kryo_init);
 224
 225static void __exit qcom_cpufreq_kryo_exit(void)
 226{
 227        platform_device_unregister(kryo_cpufreq_pdev);
 228        platform_driver_unregister(&qcom_cpufreq_kryo_driver);
 229}
 230module_exit(qcom_cpufreq_kryo_exit);
 231
 232MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver");
 233MODULE_LICENSE("GPL v2");
 234