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 __init 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                dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
 113                        PTR_ERR(speedbin_nvmem));
 114                return PTR_ERR(speedbin_nvmem);
 115        }
 116
 117        speedbin = nvmem_cell_read(speedbin_nvmem, &len);
 118        nvmem_cell_put(speedbin_nvmem);
 119        if (IS_ERR(speedbin))
 120                return PTR_ERR(speedbin);
 121
 122        switch (msm8996_version) {
 123        case MSM8996_V3:
 124                versions = 1 << (unsigned int)(*speedbin);
 125                break;
 126        case MSM8996_SG:
 127                versions = 1 << ((unsigned int)(*speedbin) + 4);
 128                break;
 129        default:
 130                BUG();
 131                break;
 132        }
 133        kfree(speedbin);
 134
 135        for_each_possible_cpu(cpu) {
 136                cpu_dev = get_cpu_device(cpu);
 137                if (NULL == cpu_dev) {
 138                        ret = -ENODEV;
 139                        goto free_opp;
 140                }
 141
 142                opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
 143                                                              &versions, 1);
 144                if (IS_ERR(opp_tables[cpu])) {
 145                        ret = PTR_ERR(opp_tables[cpu]);
 146                        dev_err(cpu_dev, "Failed to set supported hardware\n");
 147                        goto free_opp;
 148                }
 149        }
 150
 151        cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
 152                                                          NULL, 0);
 153        if (!IS_ERR(cpufreq_dt_pdev))
 154                return 0;
 155
 156        ret = PTR_ERR(cpufreq_dt_pdev);
 157        dev_err(cpu_dev, "Failed to register platform device\n");
 158
 159free_opp:
 160        for_each_possible_cpu(cpu) {
 161                if (IS_ERR_OR_NULL(opp_tables[cpu]))
 162                        break;
 163                dev_pm_opp_put_supported_hw(opp_tables[cpu]);
 164        }
 165
 166        return ret;
 167}
 168
 169static int qcom_cpufreq_kryo_remove(struct platform_device *pdev)
 170{
 171        platform_device_unregister(cpufreq_dt_pdev);
 172        return 0;
 173}
 174
 175static struct platform_driver qcom_cpufreq_kryo_driver = {
 176        .probe = qcom_cpufreq_kryo_probe,
 177        .remove = qcom_cpufreq_kryo_remove,
 178        .driver = {
 179                .name = "qcom-cpufreq-kryo",
 180        },
 181};
 182
 183static const struct of_device_id qcom_cpufreq_kryo_match_list[] __initconst = {
 184        { .compatible = "qcom,apq8096", },
 185        { .compatible = "qcom,msm8996", },
 186        {}
 187};
 188
 189/*
 190 * Since the driver depends on smem and nvmem drivers, which may
 191 * return EPROBE_DEFER, all the real activity is done in the probe,
 192 * which may be defered as well. The init here is only registering
 193 * the driver and the platform device.
 194 */
 195static int __init qcom_cpufreq_kryo_init(void)
 196{
 197        struct device_node *np = of_find_node_by_path("/");
 198        const struct of_device_id *match;
 199        int ret;
 200
 201        if (!np)
 202                return -ENODEV;
 203
 204        match = of_match_node(qcom_cpufreq_kryo_match_list, np);
 205        of_node_put(np);
 206        if (!match)
 207                return -ENODEV;
 208
 209        ret = platform_driver_register(&qcom_cpufreq_kryo_driver);
 210        if (unlikely(ret < 0))
 211                return ret;
 212
 213        kryo_cpufreq_pdev = platform_device_register_simple(
 214                "qcom-cpufreq-kryo", -1, NULL, 0);
 215        ret = PTR_ERR_OR_ZERO(kryo_cpufreq_pdev);
 216        if (0 == ret)
 217                return 0;
 218
 219        platform_driver_unregister(&qcom_cpufreq_kryo_driver);
 220        return ret;
 221}
 222module_init(qcom_cpufreq_kryo_init);
 223
 224static void __init qcom_cpufreq_kryo_exit(void)
 225{
 226        platform_device_unregister(kryo_cpufreq_pdev);
 227        platform_driver_unregister(&qcom_cpufreq_kryo_driver);
 228}
 229module_exit(qcom_cpufreq_kryo_exit);
 230
 231MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver");
 232MODULE_LICENSE("GPL v2");
 233