root/drivers/cpufreq/sti-cpufreq.c

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

DEFINITIONS

This source file includes following definitions.
  1. sti_cpufreq_fetch_major
  2. sti_cpufreq_fetch_minor
  3. sti_cpufreq_fetch_regmap_field
  4. sti_cpufreq_match
  5. sti_cpufreq_set_opp_info
  6. sti_cpufreq_fetch_syscon_registers
  7. sti_cpufreq_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Match running platform with pre-defined OPP values for CPUFreq
   4  *
   5  * Author: Ajit Pal Singh <ajitpal.singh@st.com>
   6  *         Lee Jones <lee.jones@linaro.org>
   7  *
   8  * Copyright (C) 2015 STMicroelectronics (R&D) Limited
   9  */
  10 
  11 #include <linux/cpu.h>
  12 #include <linux/io.h>
  13 #include <linux/mfd/syscon.h>
  14 #include <linux/module.h>
  15 #include <linux/of.h>
  16 #include <linux/of_platform.h>
  17 #include <linux/pm_opp.h>
  18 #include <linux/regmap.h>
  19 
  20 #define VERSION_ELEMENTS        3
  21 #define MAX_PCODE_NAME_LEN      7
  22 
  23 #define VERSION_SHIFT           28
  24 #define HW_INFO_INDEX           1
  25 #define MAJOR_ID_INDEX          1
  26 #define MINOR_ID_INDEX          2
  27 
  28 /*
  29  * Only match on "suitable for ALL versions" entries
  30  *
  31  * This will be used with the BIT() macro.  It sets the
  32  * top bit of a 32bit value and is equal to 0x80000000.
  33  */
  34 #define DEFAULT_VERSION         31
  35 
  36 enum {
  37         PCODE = 0,
  38         SUBSTRATE,
  39         DVFS_MAX_REGFIELDS,
  40 };
  41 
  42 /**
  43  * ST CPUFreq Driver Data
  44  *
  45  * @cpu_node            CPU's OF node
  46  * @syscfg_eng          Engineering Syscon register map
  47  * @regmap              Syscon register map
  48  */
  49 static struct sti_cpufreq_ddata {
  50         struct device *cpu;
  51         struct regmap *syscfg_eng;
  52         struct regmap *syscfg;
  53 } ddata;
  54 
  55 static int sti_cpufreq_fetch_major(void) {
  56         struct device_node *np = ddata.cpu->of_node;
  57         struct device *dev = ddata.cpu;
  58         unsigned int major_offset;
  59         unsigned int socid;
  60         int ret;
  61 
  62         ret = of_property_read_u32_index(np, "st,syscfg",
  63                                          MAJOR_ID_INDEX, &major_offset);
  64         if (ret) {
  65                 dev_err(dev, "No major number offset provided in %pOF [%d]\n",
  66                         np, ret);
  67                 return ret;
  68         }
  69 
  70         ret = regmap_read(ddata.syscfg, major_offset, &socid);
  71         if (ret) {
  72                 dev_err(dev, "Failed to read major number from syscon [%d]\n",
  73                         ret);
  74                 return ret;
  75         }
  76 
  77         return ((socid >> VERSION_SHIFT) & 0xf) + 1;
  78 }
  79 
  80 static int sti_cpufreq_fetch_minor(void)
  81 {
  82         struct device *dev = ddata.cpu;
  83         struct device_node *np = dev->of_node;
  84         unsigned int minor_offset;
  85         unsigned int minid;
  86         int ret;
  87 
  88         ret = of_property_read_u32_index(np, "st,syscfg-eng",
  89                                          MINOR_ID_INDEX, &minor_offset);
  90         if (ret) {
  91                 dev_err(dev,
  92                         "No minor number offset provided %pOF [%d]\n",
  93                         np, ret);
  94                 return ret;
  95         }
  96 
  97         ret = regmap_read(ddata.syscfg_eng, minor_offset, &minid);
  98         if (ret) {
  99                 dev_err(dev,
 100                         "Failed to read the minor number from syscon [%d]\n",
 101                         ret);
 102                 return ret;
 103         }
 104 
 105         return minid & 0xf;
 106 }
 107 
 108 static int sti_cpufreq_fetch_regmap_field(const struct reg_field *reg_fields,
 109                                           int hw_info_offset, int field)
 110 {
 111         struct regmap_field *regmap_field;
 112         struct reg_field reg_field = reg_fields[field];
 113         struct device *dev = ddata.cpu;
 114         unsigned int value;
 115         int ret;
 116 
 117         reg_field.reg = hw_info_offset;
 118         regmap_field = devm_regmap_field_alloc(dev,
 119                                                ddata.syscfg_eng,
 120                                                reg_field);
 121         if (IS_ERR(regmap_field)) {
 122                 dev_err(dev, "Failed to allocate reg field\n");
 123                 return PTR_ERR(regmap_field);
 124         }
 125 
 126         ret = regmap_field_read(regmap_field, &value);
 127         if (ret) {
 128                 dev_err(dev, "Failed to read %s code\n",
 129                         field ? "SUBSTRATE" : "PCODE");
 130                 return ret;
 131         }
 132 
 133         return value;
 134 }
 135 
 136 static const struct reg_field sti_stih407_dvfs_regfields[DVFS_MAX_REGFIELDS] = {
 137         [PCODE]         = REG_FIELD(0, 16, 19),
 138         [SUBSTRATE]     = REG_FIELD(0, 0, 2),
 139 };
 140 
 141 static const struct reg_field *sti_cpufreq_match(void)
 142 {
 143         if (of_machine_is_compatible("st,stih407") ||
 144             of_machine_is_compatible("st,stih410"))
 145                 return sti_stih407_dvfs_regfields;
 146 
 147         return NULL;
 148 }
 149 
 150 static int sti_cpufreq_set_opp_info(void)
 151 {
 152         struct device *dev = ddata.cpu;
 153         struct device_node *np = dev->of_node;
 154         const struct reg_field *reg_fields;
 155         unsigned int hw_info_offset;
 156         unsigned int version[VERSION_ELEMENTS];
 157         int pcode, substrate, major, minor;
 158         int ret;
 159         char name[MAX_PCODE_NAME_LEN];
 160         struct opp_table *opp_table;
 161 
 162         reg_fields = sti_cpufreq_match();
 163         if (!reg_fields) {
 164                 dev_err(dev, "This SoC doesn't support voltage scaling\n");
 165                 return -ENODEV;
 166         }
 167 
 168         ret = of_property_read_u32_index(np, "st,syscfg-eng",
 169                                          HW_INFO_INDEX, &hw_info_offset);
 170         if (ret) {
 171                 dev_warn(dev, "Failed to read HW info offset from DT\n");
 172                 substrate = DEFAULT_VERSION;
 173                 pcode = 0;
 174                 goto use_defaults;
 175         }
 176 
 177         pcode = sti_cpufreq_fetch_regmap_field(reg_fields,
 178                                                hw_info_offset,
 179                                                PCODE);
 180         if (pcode < 0) {
 181                 dev_warn(dev, "Failed to obtain process code\n");
 182                 /* Use default pcode */
 183                 pcode = 0;
 184         }
 185 
 186         substrate = sti_cpufreq_fetch_regmap_field(reg_fields,
 187                                                    hw_info_offset,
 188                                                    SUBSTRATE);
 189         if (substrate) {
 190                 dev_warn(dev, "Failed to obtain substrate code\n");
 191                 /* Use default substrate */
 192                 substrate = DEFAULT_VERSION;
 193         }
 194 
 195 use_defaults:
 196         major = sti_cpufreq_fetch_major();
 197         if (major < 0) {
 198                 dev_err(dev, "Failed to obtain major version\n");
 199                 /* Use default major number */
 200                 major = DEFAULT_VERSION;
 201         }
 202 
 203         minor = sti_cpufreq_fetch_minor();
 204         if (minor < 0) {
 205                 dev_err(dev, "Failed to obtain minor version\n");
 206                 /* Use default minor number */
 207                 minor = DEFAULT_VERSION;
 208         }
 209 
 210         snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode);
 211 
 212         opp_table = dev_pm_opp_set_prop_name(dev, name);
 213         if (IS_ERR(opp_table)) {
 214                 dev_err(dev, "Failed to set prop name\n");
 215                 return PTR_ERR(opp_table);
 216         }
 217 
 218         version[0] = BIT(major);
 219         version[1] = BIT(minor);
 220         version[2] = BIT(substrate);
 221 
 222         opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
 223         if (IS_ERR(opp_table)) {
 224                 dev_err(dev, "Failed to set supported hardware\n");
 225                 return PTR_ERR(opp_table);
 226         }
 227 
 228         dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
 229                 pcode, major, minor, substrate);
 230         dev_dbg(dev, "version[0]: %x version[1]: %x version[2]: %x\n",
 231                 version[0], version[1], version[2]);
 232 
 233         return 0;
 234 }
 235 
 236 static int sti_cpufreq_fetch_syscon_registers(void)
 237 {
 238         struct device *dev = ddata.cpu;
 239         struct device_node *np = dev->of_node;
 240 
 241         ddata.syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
 242         if (IS_ERR(ddata.syscfg)) {
 243                 dev_err(dev,  "\"st,syscfg\" not supplied\n");
 244                 return PTR_ERR(ddata.syscfg);
 245         }
 246 
 247         ddata.syscfg_eng = syscon_regmap_lookup_by_phandle(np, "st,syscfg-eng");
 248         if (IS_ERR(ddata.syscfg_eng)) {
 249                 dev_err(dev, "\"st,syscfg-eng\" not supplied\n");
 250                 return PTR_ERR(ddata.syscfg_eng);
 251         }
 252 
 253         return 0;
 254 }
 255 
 256 static int sti_cpufreq_init(void)
 257 {
 258         int ret;
 259 
 260         if ((!of_machine_is_compatible("st,stih407")) &&
 261                 (!of_machine_is_compatible("st,stih410")))
 262                 return -ENODEV;
 263 
 264         ddata.cpu = get_cpu_device(0);
 265         if (!ddata.cpu) {
 266                 dev_err(ddata.cpu, "Failed to get device for CPU0\n");
 267                 goto skip_voltage_scaling;
 268         }
 269 
 270         if (!of_get_property(ddata.cpu->of_node, "operating-points-v2", NULL)) {
 271                 dev_err(ddata.cpu, "OPP-v2 not supported\n");
 272                 goto skip_voltage_scaling;
 273         }
 274 
 275         ret = sti_cpufreq_fetch_syscon_registers();
 276         if (ret)
 277                 goto skip_voltage_scaling;
 278 
 279         ret = sti_cpufreq_set_opp_info();
 280         if (!ret)
 281                 goto register_cpufreq_dt;
 282 
 283 skip_voltage_scaling:
 284         dev_err(ddata.cpu, "Not doing voltage scaling\n");
 285 
 286 register_cpufreq_dt:
 287         platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
 288 
 289         return 0;
 290 }
 291 module_init(sti_cpufreq_init);
 292 
 293 MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
 294 MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
 295 MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
 296 MODULE_LICENSE("GPL v2");

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