cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mediatek-cpufreq-hw.c (8275B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2020 MediaTek Inc.
      4 */
      5
      6#include <linux/bitfield.h>
      7#include <linux/cpufreq.h>
      8#include <linux/energy_model.h>
      9#include <linux/init.h>
     10#include <linux/iopoll.h>
     11#include <linux/kernel.h>
     12#include <linux/module.h>
     13#include <linux/of_address.h>
     14#include <linux/of_platform.h>
     15#include <linux/slab.h>
     16
     17#define LUT_MAX_ENTRIES			32U
     18#define LUT_FREQ			GENMASK(11, 0)
     19#define LUT_ROW_SIZE			0x4
     20#define CPUFREQ_HW_STATUS		BIT(0)
     21#define SVS_HW_STATUS			BIT(1)
     22#define POLL_USEC			1000
     23#define TIMEOUT_USEC			300000
     24
     25enum {
     26	REG_FREQ_LUT_TABLE,
     27	REG_FREQ_ENABLE,
     28	REG_FREQ_PERF_STATE,
     29	REG_FREQ_HW_STATE,
     30	REG_EM_POWER_TBL,
     31	REG_FREQ_LATENCY,
     32
     33	REG_ARRAY_SIZE,
     34};
     35
     36struct mtk_cpufreq_data {
     37	struct cpufreq_frequency_table *table;
     38	void __iomem *reg_bases[REG_ARRAY_SIZE];
     39	struct resource *res;
     40	void __iomem *base;
     41	int nr_opp;
     42};
     43
     44static const u16 cpufreq_mtk_offsets[REG_ARRAY_SIZE] = {
     45	[REG_FREQ_LUT_TABLE]	= 0x0,
     46	[REG_FREQ_ENABLE]	= 0x84,
     47	[REG_FREQ_PERF_STATE]	= 0x88,
     48	[REG_FREQ_HW_STATE]	= 0x8c,
     49	[REG_EM_POWER_TBL]	= 0x90,
     50	[REG_FREQ_LATENCY]	= 0x110,
     51};
     52
     53static int __maybe_unused
     54mtk_cpufreq_get_cpu_power(struct device *cpu_dev, unsigned long *mW,
     55			  unsigned long *KHz)
     56{
     57	struct mtk_cpufreq_data *data;
     58	struct cpufreq_policy *policy;
     59	int i;
     60
     61	policy = cpufreq_cpu_get_raw(cpu_dev->id);
     62	if (!policy)
     63		return 0;
     64
     65	data = policy->driver_data;
     66
     67	for (i = 0; i < data->nr_opp; i++) {
     68		if (data->table[i].frequency < *KHz)
     69			break;
     70	}
     71	i--;
     72
     73	*KHz = data->table[i].frequency;
     74	*mW = readl_relaxed(data->reg_bases[REG_EM_POWER_TBL] +
     75			    i * LUT_ROW_SIZE) / 1000;
     76
     77	return 0;
     78}
     79
     80static int mtk_cpufreq_hw_target_index(struct cpufreq_policy *policy,
     81				       unsigned int index)
     82{
     83	struct mtk_cpufreq_data *data = policy->driver_data;
     84
     85	writel_relaxed(index, data->reg_bases[REG_FREQ_PERF_STATE]);
     86
     87	return 0;
     88}
     89
     90static unsigned int mtk_cpufreq_hw_get(unsigned int cpu)
     91{
     92	struct mtk_cpufreq_data *data;
     93	struct cpufreq_policy *policy;
     94	unsigned int index;
     95
     96	policy = cpufreq_cpu_get_raw(cpu);
     97	if (!policy)
     98		return 0;
     99
    100	data = policy->driver_data;
    101
    102	index = readl_relaxed(data->reg_bases[REG_FREQ_PERF_STATE]);
    103	index = min(index, LUT_MAX_ENTRIES - 1);
    104
    105	return data->table[index].frequency;
    106}
    107
    108static unsigned int mtk_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
    109					       unsigned int target_freq)
    110{
    111	struct mtk_cpufreq_data *data = policy->driver_data;
    112	unsigned int index;
    113
    114	index = cpufreq_table_find_index_dl(policy, target_freq, false);
    115
    116	writel_relaxed(index, data->reg_bases[REG_FREQ_PERF_STATE]);
    117
    118	return policy->freq_table[index].frequency;
    119}
    120
    121static int mtk_cpu_create_freq_table(struct platform_device *pdev,
    122				     struct mtk_cpufreq_data *data)
    123{
    124	struct device *dev = &pdev->dev;
    125	u32 temp, i, freq, prev_freq = 0;
    126	void __iomem *base_table;
    127
    128	data->table = devm_kcalloc(dev, LUT_MAX_ENTRIES + 1,
    129				   sizeof(*data->table), GFP_KERNEL);
    130	if (!data->table)
    131		return -ENOMEM;
    132
    133	base_table = data->reg_bases[REG_FREQ_LUT_TABLE];
    134
    135	for (i = 0; i < LUT_MAX_ENTRIES; i++) {
    136		temp = readl_relaxed(base_table + (i * LUT_ROW_SIZE));
    137		freq = FIELD_GET(LUT_FREQ, temp) * 1000;
    138
    139		if (freq == prev_freq)
    140			break;
    141
    142		data->table[i].frequency = freq;
    143
    144		dev_dbg(dev, "index=%d freq=%d\n", i, data->table[i].frequency);
    145
    146		prev_freq = freq;
    147	}
    148
    149	data->table[i].frequency = CPUFREQ_TABLE_END;
    150	data->nr_opp = i;
    151
    152	return 0;
    153}
    154
    155static int mtk_cpu_resources_init(struct platform_device *pdev,
    156				  struct cpufreq_policy *policy,
    157				  const u16 *offsets)
    158{
    159	struct mtk_cpufreq_data *data;
    160	struct device *dev = &pdev->dev;
    161	struct resource *res;
    162	void __iomem *base;
    163	int ret, i;
    164	int index;
    165
    166	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
    167	if (!data)
    168		return -ENOMEM;
    169
    170	index = of_perf_domain_get_sharing_cpumask(policy->cpu, "performance-domains",
    171						   "#performance-domain-cells",
    172						   policy->cpus);
    173	if (index < 0)
    174		return index;
    175
    176	res = platform_get_resource(pdev, IORESOURCE_MEM, index);
    177	if (!res) {
    178		dev_err(dev, "failed to get mem resource %d\n", index);
    179		return -ENODEV;
    180	}
    181
    182	if (!request_mem_region(res->start, resource_size(res), res->name)) {
    183		dev_err(dev, "failed to request resource %pR\n", res);
    184		return -EBUSY;
    185	}
    186
    187	base = ioremap(res->start, resource_size(res));
    188	if (!base) {
    189		dev_err(dev, "failed to map resource %pR\n", res);
    190		ret = -ENOMEM;
    191		goto release_region;
    192	}
    193
    194	data->base = base;
    195	data->res = res;
    196
    197	for (i = REG_FREQ_LUT_TABLE; i < REG_ARRAY_SIZE; i++)
    198		data->reg_bases[i] = base + offsets[i];
    199
    200	ret = mtk_cpu_create_freq_table(pdev, data);
    201	if (ret) {
    202		dev_info(dev, "Domain-%d failed to create freq table\n", index);
    203		return ret;
    204	}
    205
    206	policy->freq_table = data->table;
    207	policy->driver_data = data;
    208
    209	return 0;
    210release_region:
    211	release_mem_region(res->start, resource_size(res));
    212	return ret;
    213}
    214
    215static int mtk_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
    216{
    217	struct platform_device *pdev = cpufreq_get_driver_data();
    218	int sig, pwr_hw = CPUFREQ_HW_STATUS | SVS_HW_STATUS;
    219	struct mtk_cpufreq_data *data;
    220	unsigned int latency;
    221	int ret;
    222
    223	/* Get the bases of cpufreq for domains */
    224	ret = mtk_cpu_resources_init(pdev, policy, platform_get_drvdata(pdev));
    225	if (ret) {
    226		dev_info(&pdev->dev, "CPUFreq resource init failed\n");
    227		return ret;
    228	}
    229
    230	data = policy->driver_data;
    231
    232	latency = readl_relaxed(data->reg_bases[REG_FREQ_LATENCY]) * 1000;
    233	if (!latency)
    234		latency = CPUFREQ_ETERNAL;
    235
    236	policy->cpuinfo.transition_latency = latency;
    237	policy->fast_switch_possible = true;
    238
    239	/* HW should be in enabled state to proceed now */
    240	writel_relaxed(0x1, data->reg_bases[REG_FREQ_ENABLE]);
    241	if (readl_poll_timeout(data->reg_bases[REG_FREQ_HW_STATE], sig,
    242			       (sig & pwr_hw) == pwr_hw, POLL_USEC,
    243			       TIMEOUT_USEC)) {
    244		if (!(sig & CPUFREQ_HW_STATUS)) {
    245			pr_info("cpufreq hardware of CPU%d is not enabled\n",
    246				policy->cpu);
    247			return -ENODEV;
    248		}
    249
    250		pr_info("SVS of CPU%d is not enabled\n", policy->cpu);
    251	}
    252
    253	return 0;
    254}
    255
    256static int mtk_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
    257{
    258	struct mtk_cpufreq_data *data = policy->driver_data;
    259	struct resource *res = data->res;
    260	void __iomem *base = data->base;
    261
    262	/* HW should be in paused state now */
    263	writel_relaxed(0x0, data->reg_bases[REG_FREQ_ENABLE]);
    264	iounmap(base);
    265	release_mem_region(res->start, resource_size(res));
    266
    267	return 0;
    268}
    269
    270static void mtk_cpufreq_register_em(struct cpufreq_policy *policy)
    271{
    272	struct em_data_callback em_cb = EM_DATA_CB(mtk_cpufreq_get_cpu_power);
    273	struct mtk_cpufreq_data *data = policy->driver_data;
    274
    275	em_dev_register_perf_domain(get_cpu_device(policy->cpu), data->nr_opp,
    276				    &em_cb, policy->cpus, true);
    277}
    278
    279static struct cpufreq_driver cpufreq_mtk_hw_driver = {
    280	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK |
    281			  CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
    282			  CPUFREQ_IS_COOLING_DEV,
    283	.verify		= cpufreq_generic_frequency_table_verify,
    284	.target_index	= mtk_cpufreq_hw_target_index,
    285	.get		= mtk_cpufreq_hw_get,
    286	.init		= mtk_cpufreq_hw_cpu_init,
    287	.exit		= mtk_cpufreq_hw_cpu_exit,
    288	.register_em	= mtk_cpufreq_register_em,
    289	.fast_switch	= mtk_cpufreq_hw_fast_switch,
    290	.name		= "mtk-cpufreq-hw",
    291	.attr		= cpufreq_generic_attr,
    292};
    293
    294static int mtk_cpufreq_hw_driver_probe(struct platform_device *pdev)
    295{
    296	const void *data;
    297	int ret;
    298
    299	data = of_device_get_match_data(&pdev->dev);
    300	if (!data)
    301		return -EINVAL;
    302
    303	platform_set_drvdata(pdev, (void *) data);
    304	cpufreq_mtk_hw_driver.driver_data = pdev;
    305
    306	ret = cpufreq_register_driver(&cpufreq_mtk_hw_driver);
    307	if (ret)
    308		dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n");
    309
    310	return ret;
    311}
    312
    313static int mtk_cpufreq_hw_driver_remove(struct platform_device *pdev)
    314{
    315	return cpufreq_unregister_driver(&cpufreq_mtk_hw_driver);
    316}
    317
    318static const struct of_device_id mtk_cpufreq_hw_match[] = {
    319	{ .compatible = "mediatek,cpufreq-hw", .data = &cpufreq_mtk_offsets },
    320	{}
    321};
    322
    323static struct platform_driver mtk_cpufreq_hw_driver = {
    324	.probe = mtk_cpufreq_hw_driver_probe,
    325	.remove = mtk_cpufreq_hw_driver_remove,
    326	.driver = {
    327		.name = "mtk-cpufreq-hw",
    328		.of_match_table = mtk_cpufreq_hw_match,
    329	},
    330};
    331module_platform_driver(mtk_cpufreq_hw_driver);
    332
    333MODULE_AUTHOR("Hector Yuan <hector.yuan@mediatek.com>");
    334MODULE_DESCRIPTION("Mediatek cpufreq-hw driver");
    335MODULE_LICENSE("GPL v2");