File Index
Files and directories
Sign in
FILE

exynos-asv.c

File preview with syntax highlighting.

linux-6.19.14/drivers/soc/samsung/exynos-asv.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (c) 2019 Samsung Electronics Co., Ltd.
  4 *	      http://www.samsung.com/
  5 * Copyright (c) 2020 Krzysztof Kozlowski <krzk@kernel.org>
  6 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
  7 * Author: Krzysztof Kozlowski <krzk@kernel.org>
  8 *
  9 * Samsung Exynos SoC Adaptive Supply Voltage support
 10 */
 11
 12#include <linux/array_size.h>
 13#include <linux/cpu.h>
 14#include <linux/device.h>
 15#include <linux/energy_model.h>
 16#include <linux/errno.h>
 17#include <linux/of.h>
 18#include <linux/pm_opp.h>
 19#include <linux/regmap.h>
 20#include <linux/soc/samsung/exynos-chipid.h>
 21
 22#include "exynos-asv.h"
 23#include "exynos5422-asv.h"
 24
 25#define MHZ 1000000U
 26
 27static int exynos_asv_update_cpu_opps(struct exynos_asv *asv,
 28				      struct device *cpu)
 29{
 30	struct exynos_asv_subsys *subsys = NULL;
 31	struct dev_pm_opp *opp;
 32	unsigned int opp_freq;
 33	int i;
 34
 35	for (i = 0; i < ARRAY_SIZE(asv->subsys); i++) {
 36		if (of_device_is_compatible(cpu->of_node,
 37					    asv->subsys[i].cpu_dt_compat)) {
 38			subsys = &asv->subsys[i];
 39			break;
 40		}
 41	}
 42	if (!subsys)
 43		return -EINVAL;
 44
 45	for (i = 0; i < subsys->table.num_rows; i++) {
 46		unsigned int new_volt, volt;
 47		int ret;
 48
 49		opp_freq = exynos_asv_opp_get_frequency(subsys, i);
 50
 51		opp = dev_pm_opp_find_freq_exact(cpu, opp_freq * MHZ, true);
 52		if (IS_ERR(opp)) {
 53			dev_info(asv->dev, "cpu%d opp%d, freq: %u missing\n",
 54				 cpu->id, i, opp_freq);
 55
 56			continue;
 57		}
 58
 59		volt = dev_pm_opp_get_voltage(opp);
 60		new_volt = asv->opp_get_voltage(subsys, i, volt);
 61		dev_pm_opp_put(opp);
 62
 63		if (new_volt == volt)
 64			continue;
 65
 66		ret = dev_pm_opp_adjust_voltage(cpu, opp_freq * MHZ,
 67						new_volt, new_volt, new_volt);
 68		if (ret < 0)
 69			dev_err(asv->dev,
 70				"Failed to adjust OPP %u Hz/%u uV for cpu%d\n",
 71				opp_freq, new_volt, cpu->id);
 72		else
 73			dev_dbg(asv->dev,
 74				"Adjusted OPP %u Hz/%u -> %u uV, cpu%d\n",
 75				opp_freq, volt, new_volt, cpu->id);
 76	}
 77
 78	return 0;
 79}
 80
 81static int exynos_asv_update_opps(struct exynos_asv *asv)
 82{
 83	struct opp_table *last_opp_table = NULL;
 84	struct device *cpu;
 85	int ret, cpuid;
 86
 87	for_each_possible_cpu(cpuid) {
 88		struct opp_table *opp_table;
 89
 90		cpu = get_cpu_device(cpuid);
 91		if (!cpu)
 92			continue;
 93
 94		opp_table = dev_pm_opp_get_opp_table(cpu);
 95		if (IS_ERR(opp_table))
 96			continue;
 97
 98		if (!last_opp_table || opp_table != last_opp_table) {
 99			last_opp_table = opp_table;
100
101			ret = exynos_asv_update_cpu_opps(asv, cpu);
102			if (!ret) {
103				/*
104				 * Update EM power values since OPP
105				 * voltage values may have changed.
106				 */
107				em_dev_update_chip_binning(cpu);
108			} else {
109				dev_err(asv->dev, "Couldn't udate OPPs for cpu%d\n",
110					cpuid);
111			}
112		}
113
114		dev_pm_opp_put_opp_table(opp_table);
115	}
116
117	return	0;
118}
119
120int exynos_asv_init(struct device *dev, struct regmap *regmap)
121{
122	int (*probe_func)(struct exynos_asv *asv);
123	struct exynos_asv *asv;
124	struct device *cpu_dev;
125	u32 product_id = 0;
126	int ret, i;
127
128	asv = devm_kzalloc(dev, sizeof(*asv), GFP_KERNEL);
129	if (!asv)
130		return -ENOMEM;
131
132	asv->chipid_regmap = regmap;
133	asv->dev = dev;
134	ret = regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_PRO_ID,
135			  &product_id);
136	if (ret < 0) {
137		dev_err(dev, "Cannot read revision from ChipID: %d\n", ret);
138		return -ENODEV;
139	}
140
141	switch (product_id & EXYNOS_MASK) {
142	case 0xE5422000:
143		probe_func = exynos5422_asv_init;
144		break;
145	default:
146		dev_dbg(dev, "No ASV support for this SoC\n");
147		devm_kfree(dev, asv);
148		return 0;
149	}
150
151	cpu_dev = get_cpu_device(0);
152	ret = dev_pm_opp_get_opp_count(cpu_dev);
153	if (ret < 0)
154		return -EPROBE_DEFER;
155
156	ret = of_property_read_u32(dev->of_node, "samsung,asv-bin",
157				   &asv->of_bin);
158	if (ret < 0)
159		asv->of_bin = -EINVAL;
160
161	for (i = 0; i < ARRAY_SIZE(asv->subsys); i++)
162		asv->subsys[i].asv = asv;
163
164	ret = probe_func(asv);
165	if (ret < 0)
166		return ret;
167
168	return exynos_asv_update_opps(asv);
169}
~/ Read-only file preview.