brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · b15b314 Raw
305 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Match running platform with pre-defined OPP values for CPUFreq4 *5 * Author: Ajit Pal Singh <ajitpal.singh@st.com>6 *         Lee Jones <lee.jones@linaro.org>7 *8 * Copyright (C) 2015 STMicroelectronics (R&D) Limited9 */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/platform_device.h>17#include <linux/pm_opp.h>18#include <linux/regmap.h>19 20#define VERSION_ELEMENTS	321#define MAX_PCODE_NAME_LEN	1622 23#define VERSION_SHIFT		2824#define HW_INFO_INDEX		125#define MAJOR_ID_INDEX		126#define MINOR_ID_INDEX		227 28/*29 * Only match on "suitable for ALL versions" entries30 *31 * This will be used with the BIT() macro.  It sets the32 * top bit of a 32bit value and is equal to 0x80000000.33 */34#define DEFAULT_VERSION		3135 36enum {37	PCODE = 0,38	SUBSTRATE,39	DVFS_MAX_REGFIELDS,40};41 42/**43 * struct sti_cpufreq_ddata - ST CPUFreq Driver Data44 *45 * @cpu:		CPU's OF node46 * @syscfg_eng:		Engineering Syscon register map47 * @syscfg:		Syscon register map48 */49static struct sti_cpufreq_ddata {50	struct device *cpu;51	struct regmap *syscfg_eng;52	struct regmap *syscfg;53} ddata;54 55static 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 80static 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 108static 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 136static 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 141static 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	    of_machine_is_compatible("st,stih418"))146		return sti_stih407_dvfs_regfields;147 148	return NULL;149}150 151static int sti_cpufreq_set_opp_info(void)152{153	struct device *dev = ddata.cpu;154	struct device_node *np = dev->of_node;155	const struct reg_field *reg_fields;156	unsigned int hw_info_offset;157	unsigned int version[VERSION_ELEMENTS];158	int pcode, substrate, major, minor;159	int opp_token, ret;160	char name[MAX_PCODE_NAME_LEN];161	struct dev_pm_opp_config config = {162		.supported_hw = version,163		.supported_hw_count = ARRAY_SIZE(version),164		.prop_name = name,165	};166 167	reg_fields = sti_cpufreq_match();168	if (!reg_fields) {169		dev_err(dev, "This SoC doesn't support voltage scaling\n");170		return -ENODEV;171	}172 173	ret = of_property_read_u32_index(np, "st,syscfg-eng",174					 HW_INFO_INDEX, &hw_info_offset);175	if (ret) {176		dev_warn(dev, "Failed to read HW info offset from DT\n");177		substrate = DEFAULT_VERSION;178		pcode = 0;179		goto use_defaults;180	}181 182	pcode = sti_cpufreq_fetch_regmap_field(reg_fields,183					       hw_info_offset,184					       PCODE);185	if (pcode < 0) {186		dev_warn(dev, "Failed to obtain process code\n");187		/* Use default pcode */188		pcode = 0;189	}190 191	substrate = sti_cpufreq_fetch_regmap_field(reg_fields,192						   hw_info_offset,193						   SUBSTRATE);194	if (substrate) {195		dev_warn(dev, "Failed to obtain substrate code\n");196		/* Use default substrate */197		substrate = DEFAULT_VERSION;198	}199 200use_defaults:201	major = sti_cpufreq_fetch_major();202	if (major < 0) {203		dev_err(dev, "Failed to obtain major version\n");204		/* Use default major number */205		major = DEFAULT_VERSION;206	}207 208	minor = sti_cpufreq_fetch_minor();209	if (minor < 0) {210		dev_err(dev, "Failed to obtain minor version\n");211		/* Use default minor number */212		minor = DEFAULT_VERSION;213	}214 215	snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode);216 217	version[0] = BIT(major);218	version[1] = BIT(minor);219	version[2] = BIT(substrate);220 221	opp_token = dev_pm_opp_set_config(dev, &config);222	if (opp_token < 0) {223		dev_err(dev, "Failed to set OPP config\n");224		return opp_token;225	}226 227	dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",228		pcode, major, minor, substrate);229	dev_dbg(dev, "version[0]: %x version[1]: %x version[2]: %x\n",230		version[0], version[1], version[2]);231 232	return 0;233}234 235static int sti_cpufreq_fetch_syscon_registers(void)236{237	struct device *dev = ddata.cpu;238	struct device_node *np = dev->of_node;239 240	ddata.syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");241	if (IS_ERR(ddata.syscfg)) {242		dev_err(dev,  "\"st,syscfg\" not supplied\n");243		return PTR_ERR(ddata.syscfg);244	}245 246	ddata.syscfg_eng = syscon_regmap_lookup_by_phandle(np, "st,syscfg-eng");247	if (IS_ERR(ddata.syscfg_eng)) {248		dev_err(dev, "\"st,syscfg-eng\" not supplied\n");249		return PTR_ERR(ddata.syscfg_eng);250	}251 252	return 0;253}254 255static int __init sti_cpufreq_init(void)256{257	int ret;258 259	if ((!of_machine_is_compatible("st,stih407")) &&260		(!of_machine_is_compatible("st,stih410")) &&261		(!of_machine_is_compatible("st,stih418")))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_property_present(ddata.cpu->of_node, "operating-points-v2")) {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 283skip_voltage_scaling:284	dev_err(ddata.cpu, "Not doing voltage scaling\n");285 286register_cpufreq_dt:287	platform_device_register_simple("cpufreq-dt", -1, NULL, 0);288 289	return 0;290}291module_init(sti_cpufreq_init);292 293static const struct of_device_id __maybe_unused sti_cpufreq_of_match[] = {294	{ .compatible = "st,stih407" },295	{ .compatible = "st,stih410" },296	{ .compatible = "st,stih418" },297	{ },298};299MODULE_DEVICE_TABLE(of, sti_cpufreq_of_match);300 301MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");302MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");303MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");304MODULE_LICENSE("GPL v2");305