root/drivers/cpufreq/ti-cpufreq.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. amx3_efuse_xlate
  2. dra7_efuse_xlate
  3. ti_cpufreq_get_efuse
  4. ti_cpufreq_get_rev
  5. ti_cpufreq_setup_syscon_register
  6. ti_cpufreq_match_node
  7. ti_cpufreq_probe
  8. ti_cpufreq_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * TI CPUFreq/OPP hw-supported driver
   4  *
   5  * Copyright (C) 2016-2017 Texas Instruments, Inc.
   6  *       Dave Gerlach <d-gerlach@ti.com>
   7  */
   8 
   9 #include <linux/cpu.h>
  10 #include <linux/io.h>
  11 #include <linux/mfd/syscon.h>
  12 #include <linux/module.h>
  13 #include <linux/init.h>
  14 #include <linux/of.h>
  15 #include <linux/of_platform.h>
  16 #include <linux/pm_opp.h>
  17 #include <linux/regmap.h>
  18 #include <linux/slab.h>
  19 
  20 #define REVISION_MASK                           0xF
  21 #define REVISION_SHIFT                          28
  22 
  23 #define AM33XX_800M_ARM_MPU_MAX_FREQ            0x1E2F
  24 #define AM43XX_600M_ARM_MPU_MAX_FREQ            0xFFA
  25 
  26 #define DRA7_EFUSE_HAS_OD_MPU_OPP               11
  27 #define DRA7_EFUSE_HAS_HIGH_MPU_OPP             15
  28 #define DRA7_EFUSE_HAS_ALL_MPU_OPP              23
  29 
  30 #define DRA7_EFUSE_NOM_MPU_OPP                  BIT(0)
  31 #define DRA7_EFUSE_OD_MPU_OPP                   BIT(1)
  32 #define DRA7_EFUSE_HIGH_MPU_OPP                 BIT(2)
  33 
  34 #define VERSION_COUNT                           2
  35 
  36 struct ti_cpufreq_data;
  37 
  38 struct ti_cpufreq_soc_data {
  39         unsigned long (*efuse_xlate)(struct ti_cpufreq_data *opp_data,
  40                                      unsigned long efuse);
  41         unsigned long efuse_fallback;
  42         unsigned long efuse_offset;
  43         unsigned long efuse_mask;
  44         unsigned long efuse_shift;
  45         unsigned long rev_offset;
  46         bool multi_regulator;
  47 };
  48 
  49 struct ti_cpufreq_data {
  50         struct device *cpu_dev;
  51         struct device_node *opp_node;
  52         struct regmap *syscon;
  53         const struct ti_cpufreq_soc_data *soc_data;
  54         struct opp_table *opp_table;
  55 };
  56 
  57 static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data,
  58                                       unsigned long efuse)
  59 {
  60         if (!efuse)
  61                 efuse = opp_data->soc_data->efuse_fallback;
  62         /* AM335x and AM437x use "OPP disable" bits, so invert */
  63         return ~efuse;
  64 }
  65 
  66 static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data,
  67                                       unsigned long efuse)
  68 {
  69         unsigned long calculated_efuse = DRA7_EFUSE_NOM_MPU_OPP;
  70 
  71         /*
  72          * The efuse on dra7 and am57 parts contains a specific
  73          * value indicating the highest available OPP.
  74          */
  75 
  76         switch (efuse) {
  77         case DRA7_EFUSE_HAS_ALL_MPU_OPP:
  78         case DRA7_EFUSE_HAS_HIGH_MPU_OPP:
  79                 calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP;
  80                 /* Fall through */
  81         case DRA7_EFUSE_HAS_OD_MPU_OPP:
  82                 calculated_efuse |= DRA7_EFUSE_OD_MPU_OPP;
  83         }
  84 
  85         return calculated_efuse;
  86 }
  87 
  88 static struct ti_cpufreq_soc_data am3x_soc_data = {
  89         .efuse_xlate = amx3_efuse_xlate,
  90         .efuse_fallback = AM33XX_800M_ARM_MPU_MAX_FREQ,
  91         .efuse_offset = 0x07fc,
  92         .efuse_mask = 0x1fff,
  93         .rev_offset = 0x600,
  94         .multi_regulator = false,
  95 };
  96 
  97 static struct ti_cpufreq_soc_data am4x_soc_data = {
  98         .efuse_xlate = amx3_efuse_xlate,
  99         .efuse_fallback = AM43XX_600M_ARM_MPU_MAX_FREQ,
 100         .efuse_offset = 0x0610,
 101         .efuse_mask = 0x3f,
 102         .rev_offset = 0x600,
 103         .multi_regulator = false,
 104 };
 105 
 106 static struct ti_cpufreq_soc_data dra7_soc_data = {
 107         .efuse_xlate = dra7_efuse_xlate,
 108         .efuse_offset = 0x020c,
 109         .efuse_mask = 0xf80000,
 110         .efuse_shift = 19,
 111         .rev_offset = 0x204,
 112         .multi_regulator = true,
 113 };
 114 
 115 /**
 116  * ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC
 117  * @opp_data: pointer to ti_cpufreq_data context
 118  * @efuse_value: Set to the value parsed from efuse
 119  *
 120  * Returns error code if efuse not read properly.
 121  */
 122 static int ti_cpufreq_get_efuse(struct ti_cpufreq_data *opp_data,
 123                                 u32 *efuse_value)
 124 {
 125         struct device *dev = opp_data->cpu_dev;
 126         u32 efuse;
 127         int ret;
 128 
 129         ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset,
 130                           &efuse);
 131         if (ret) {
 132                 dev_err(dev,
 133                         "Failed to read the efuse value from syscon: %d\n",
 134                         ret);
 135                 return ret;
 136         }
 137 
 138         efuse = (efuse & opp_data->soc_data->efuse_mask);
 139         efuse >>= opp_data->soc_data->efuse_shift;
 140 
 141         *efuse_value = opp_data->soc_data->efuse_xlate(opp_data, efuse);
 142 
 143         return 0;
 144 }
 145 
 146 /**
 147  * ti_cpufreq_get_rev() - Parse and return rev value present on SoC
 148  * @opp_data: pointer to ti_cpufreq_data context
 149  * @revision_value: Set to the value parsed from revision register
 150  *
 151  * Returns error code if revision not read properly.
 152  */
 153 static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data,
 154                               u32 *revision_value)
 155 {
 156         struct device *dev = opp_data->cpu_dev;
 157         u32 revision;
 158         int ret;
 159 
 160         ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset,
 161                           &revision);
 162         if (ret) {
 163                 dev_err(dev,
 164                         "Failed to read the revision number from syscon: %d\n",
 165                         ret);
 166                 return ret;
 167         }
 168 
 169         *revision_value = BIT((revision >> REVISION_SHIFT) & REVISION_MASK);
 170 
 171         return 0;
 172 }
 173 
 174 static int ti_cpufreq_setup_syscon_register(struct ti_cpufreq_data *opp_data)
 175 {
 176         struct device *dev = opp_data->cpu_dev;
 177         struct device_node *np = opp_data->opp_node;
 178 
 179         opp_data->syscon = syscon_regmap_lookup_by_phandle(np,
 180                                                         "syscon");
 181         if (IS_ERR(opp_data->syscon)) {
 182                 dev_err(dev,
 183                         "\"syscon\" is missing, cannot use OPPv2 table.\n");
 184                 return PTR_ERR(opp_data->syscon);
 185         }
 186 
 187         return 0;
 188 }
 189 
 190 static const struct of_device_id ti_cpufreq_of_match[] = {
 191         { .compatible = "ti,am33xx", .data = &am3x_soc_data, },
 192         { .compatible = "ti,am43", .data = &am4x_soc_data, },
 193         { .compatible = "ti,dra7", .data = &dra7_soc_data },
 194         {},
 195 };
 196 
 197 static const struct of_device_id *ti_cpufreq_match_node(void)
 198 {
 199         struct device_node *np;
 200         const struct of_device_id *match;
 201 
 202         np = of_find_node_by_path("/");
 203         match = of_match_node(ti_cpufreq_of_match, np);
 204         of_node_put(np);
 205 
 206         return match;
 207 }
 208 
 209 static int ti_cpufreq_probe(struct platform_device *pdev)
 210 {
 211         u32 version[VERSION_COUNT];
 212         const struct of_device_id *match;
 213         struct opp_table *ti_opp_table;
 214         struct ti_cpufreq_data *opp_data;
 215         const char * const reg_names[] = {"vdd", "vbb"};
 216         int ret;
 217 
 218         match = dev_get_platdata(&pdev->dev);
 219         if (!match)
 220                 return -ENODEV;
 221 
 222         opp_data = devm_kzalloc(&pdev->dev, sizeof(*opp_data), GFP_KERNEL);
 223         if (!opp_data)
 224                 return -ENOMEM;
 225 
 226         opp_data->soc_data = match->data;
 227 
 228         opp_data->cpu_dev = get_cpu_device(0);
 229         if (!opp_data->cpu_dev) {
 230                 pr_err("%s: Failed to get device for CPU0\n", __func__);
 231                 return -ENODEV;
 232         }
 233 
 234         opp_data->opp_node = dev_pm_opp_of_get_opp_desc_node(opp_data->cpu_dev);
 235         if (!opp_data->opp_node) {
 236                 dev_info(opp_data->cpu_dev,
 237                          "OPP-v2 not supported, cpufreq-dt will attempt to use legacy tables.\n");
 238                 goto register_cpufreq_dt;
 239         }
 240 
 241         ret = ti_cpufreq_setup_syscon_register(opp_data);
 242         if (ret)
 243                 goto fail_put_node;
 244 
 245         /*
 246          * OPPs determine whether or not they are supported based on
 247          * two metrics:
 248          *      0 - SoC Revision
 249          *      1 - eFuse value
 250          */
 251         ret = ti_cpufreq_get_rev(opp_data, &version[0]);
 252         if (ret)
 253                 goto fail_put_node;
 254 
 255         ret = ti_cpufreq_get_efuse(opp_data, &version[1]);
 256         if (ret)
 257                 goto fail_put_node;
 258 
 259         ti_opp_table = dev_pm_opp_set_supported_hw(opp_data->cpu_dev,
 260                                                    version, VERSION_COUNT);
 261         if (IS_ERR(ti_opp_table)) {
 262                 dev_err(opp_data->cpu_dev,
 263                         "Failed to set supported hardware\n");
 264                 ret = PTR_ERR(ti_opp_table);
 265                 goto fail_put_node;
 266         }
 267 
 268         opp_data->opp_table = ti_opp_table;
 269 
 270         if (opp_data->soc_data->multi_regulator) {
 271                 ti_opp_table = dev_pm_opp_set_regulators(opp_data->cpu_dev,
 272                                                          reg_names,
 273                                                          ARRAY_SIZE(reg_names));
 274                 if (IS_ERR(ti_opp_table)) {
 275                         dev_pm_opp_put_supported_hw(opp_data->opp_table);
 276                         ret =  PTR_ERR(ti_opp_table);
 277                         goto fail_put_node;
 278                 }
 279         }
 280 
 281         of_node_put(opp_data->opp_node);
 282 register_cpufreq_dt:
 283         platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
 284 
 285         return 0;
 286 
 287 fail_put_node:
 288         of_node_put(opp_data->opp_node);
 289 
 290         return ret;
 291 }
 292 
 293 static int ti_cpufreq_init(void)
 294 {
 295         const struct of_device_id *match;
 296 
 297         /* Check to ensure we are on a compatible platform */
 298         match = ti_cpufreq_match_node();
 299         if (match)
 300                 platform_device_register_data(NULL, "ti-cpufreq", -1, match,
 301                                               sizeof(*match));
 302 
 303         return 0;
 304 }
 305 module_init(ti_cpufreq_init);
 306 
 307 static struct platform_driver ti_cpufreq_driver = {
 308         .probe = ti_cpufreq_probe,
 309         .driver = {
 310                 .name = "ti-cpufreq",
 311         },
 312 };
 313 builtin_platform_driver(ti_cpufreq_driver);
 314 
 315 MODULE_DESCRIPTION("TI CPUFreq/OPP hw-supported driver");
 316 MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
 317 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */