brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 7b8fcfa Raw
286 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved4 */5 6#include <linux/cpufreq.h>7#include <linux/dma-mapping.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/platform_device.h>11 12#include <soc/tegra/bpmp.h>13#include <soc/tegra/bpmp-abi.h>14 15#define TEGRA186_NUM_CLUSTERS		216#define EDVD_OFFSET_A57(core)		((SZ_64K * 6) + (0x20 + (core) * 0x4))17#define EDVD_OFFSET_DENVER(core)	((SZ_64K * 7) + (0x20 + (core) * 0x4))18#define EDVD_CORE_VOLT_FREQ_F_SHIFT	019#define EDVD_CORE_VOLT_FREQ_F_MASK	0xffff20#define EDVD_CORE_VOLT_FREQ_V_SHIFT	1621 22struct tegra186_cpufreq_cpu {23	unsigned int bpmp_cluster_id;24	unsigned int edvd_offset;25};26 27static const struct tegra186_cpufreq_cpu tegra186_cpus[] = {28	/* CPU0 - A57 Cluster */29	{30		.bpmp_cluster_id = 1,31		.edvd_offset = EDVD_OFFSET_A57(0)32	},33	/* CPU1 - Denver Cluster */34	{35		.bpmp_cluster_id = 0,36		.edvd_offset = EDVD_OFFSET_DENVER(0)37	},38	/* CPU2 - Denver Cluster */39	{40		.bpmp_cluster_id = 0,41		.edvd_offset = EDVD_OFFSET_DENVER(1)42	},43	/* CPU3 - A57 Cluster */44	{45		.bpmp_cluster_id = 1,46		.edvd_offset = EDVD_OFFSET_A57(1)47	},48	/* CPU4 - A57 Cluster */49	{50		.bpmp_cluster_id = 1,51		.edvd_offset = EDVD_OFFSET_A57(2)52	},53	/* CPU5 - A57 Cluster */54	{55		.bpmp_cluster_id = 1,56		.edvd_offset = EDVD_OFFSET_A57(3)57	},58};59 60struct tegra186_cpufreq_cluster {61	struct cpufreq_frequency_table *table;62	u32 ref_clk_khz;63	u32 div;64};65 66struct tegra186_cpufreq_data {67	void __iomem *regs;68	const struct tegra186_cpufreq_cpu *cpus;69	struct tegra186_cpufreq_cluster clusters[];70};71 72static int tegra186_cpufreq_init(struct cpufreq_policy *policy)73{74	struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();75	unsigned int cluster = data->cpus[policy->cpu].bpmp_cluster_id;76 77	policy->freq_table = data->clusters[cluster].table;78	policy->cpuinfo.transition_latency = 300 * 1000;79	policy->driver_data = NULL;80 81	return 0;82}83 84static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,85				       unsigned int index)86{87	struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();88	struct cpufreq_frequency_table *tbl = policy->freq_table + index;89	unsigned int edvd_offset = data->cpus[policy->cpu].edvd_offset;90	u32 edvd_val = tbl->driver_data;91 92	writel(edvd_val, data->regs + edvd_offset);93 94	return 0;95}96 97static unsigned int tegra186_cpufreq_get(unsigned int cpu)98{99	struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();100	struct tegra186_cpufreq_cluster *cluster;101	struct cpufreq_policy *policy;102	unsigned int edvd_offset, cluster_id;103	u32 ndiv;104 105	policy = cpufreq_cpu_get(cpu);106	if (!policy)107		return 0;108 109	edvd_offset = data->cpus[policy->cpu].edvd_offset;110	ndiv = readl(data->regs + edvd_offset) & EDVD_CORE_VOLT_FREQ_F_MASK;111	cluster_id = data->cpus[policy->cpu].bpmp_cluster_id;112	cluster = &data->clusters[cluster_id];113	cpufreq_cpu_put(policy);114 115	return (cluster->ref_clk_khz * ndiv) / cluster->div;116}117 118static struct cpufreq_driver tegra186_cpufreq_driver = {119	.name = "tegra186",120	.flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY |121			CPUFREQ_NEED_INITIAL_FREQ_CHECK,122	.get = tegra186_cpufreq_get,123	.verify = cpufreq_generic_frequency_table_verify,124	.target_index = tegra186_cpufreq_set_target,125	.init = tegra186_cpufreq_init,126	.attr = cpufreq_generic_attr,127};128 129static struct cpufreq_frequency_table *init_vhint_table(130	struct platform_device *pdev, struct tegra_bpmp *bpmp,131	struct tegra186_cpufreq_cluster *cluster, unsigned int cluster_id)132{133	struct cpufreq_frequency_table *table;134	struct mrq_cpu_vhint_request req;135	struct tegra_bpmp_message msg;136	struct cpu_vhint_data *data;137	int err, i, j, num_rates = 0;138	dma_addr_t phys;139	void *virt;140 141	virt = dma_alloc_coherent(bpmp->dev, sizeof(*data), &phys,142				  GFP_KERNEL);143	if (!virt)144		return ERR_PTR(-ENOMEM);145 146	data = (struct cpu_vhint_data *)virt;147 148	memset(&req, 0, sizeof(req));149	req.addr = phys;150	req.cluster_id = cluster_id;151 152	memset(&msg, 0, sizeof(msg));153	msg.mrq = MRQ_CPU_VHINT;154	msg.tx.data = &req;155	msg.tx.size = sizeof(req);156 157	err = tegra_bpmp_transfer(bpmp, &msg);158	if (err) {159		table = ERR_PTR(err);160		goto free;161	}162	if (msg.rx.ret) {163		table = ERR_PTR(-EINVAL);164		goto free;165	}166 167	for (i = data->vfloor; i <= data->vceil; i++) {168		u16 ndiv = data->ndiv[i];169 170		if (ndiv < data->ndiv_min || ndiv > data->ndiv_max)171			continue;172 173		/* Only store lowest voltage index for each rate */174		if (i > 0 && ndiv == data->ndiv[i - 1])175			continue;176 177		num_rates++;178	}179 180	table = devm_kcalloc(&pdev->dev, num_rates + 1, sizeof(*table),181			     GFP_KERNEL);182	if (!table) {183		table = ERR_PTR(-ENOMEM);184		goto free;185	}186 187	cluster->ref_clk_khz = data->ref_clk_hz / 1000;188	cluster->div = data->pdiv * data->mdiv;189 190	for (i = data->vfloor, j = 0; i <= data->vceil; i++) {191		struct cpufreq_frequency_table *point;192		u16 ndiv = data->ndiv[i];193		u32 edvd_val = 0;194 195		if (ndiv < data->ndiv_min || ndiv > data->ndiv_max)196			continue;197 198		/* Only store lowest voltage index for each rate */199		if (i > 0 && ndiv == data->ndiv[i - 1])200			continue;201 202		edvd_val |= i << EDVD_CORE_VOLT_FREQ_V_SHIFT;203		edvd_val |= ndiv << EDVD_CORE_VOLT_FREQ_F_SHIFT;204 205		point = &table[j++];206		point->driver_data = edvd_val;207		point->frequency = (cluster->ref_clk_khz * ndiv) / cluster->div;208	}209 210	table[j].frequency = CPUFREQ_TABLE_END;211 212free:213	dma_free_coherent(bpmp->dev, sizeof(*data), virt, phys);214 215	return table;216}217 218static int tegra186_cpufreq_probe(struct platform_device *pdev)219{220	struct tegra186_cpufreq_data *data;221	struct tegra_bpmp *bpmp;222	unsigned int i = 0, err;223 224	data = devm_kzalloc(&pdev->dev,225			    struct_size(data, clusters, TEGRA186_NUM_CLUSTERS),226			    GFP_KERNEL);227	if (!data)228		return -ENOMEM;229 230	data->cpus = tegra186_cpus;231 232	bpmp = tegra_bpmp_get(&pdev->dev);233	if (IS_ERR(bpmp))234		return PTR_ERR(bpmp);235 236	data->regs = devm_platform_ioremap_resource(pdev, 0);237	if (IS_ERR(data->regs)) {238		err = PTR_ERR(data->regs);239		goto put_bpmp;240	}241 242	for (i = 0; i < TEGRA186_NUM_CLUSTERS; i++) {243		struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];244 245		cluster->table = init_vhint_table(pdev, bpmp, cluster, i);246		if (IS_ERR(cluster->table)) {247			err = PTR_ERR(cluster->table);248			goto put_bpmp;249		}250	}251 252	tegra186_cpufreq_driver.driver_data = data;253 254	err = cpufreq_register_driver(&tegra186_cpufreq_driver);255 256put_bpmp:257	tegra_bpmp_put(bpmp);258 259	return err;260}261 262static void tegra186_cpufreq_remove(struct platform_device *pdev)263{264	cpufreq_unregister_driver(&tegra186_cpufreq_driver);265}266 267static const struct of_device_id tegra186_cpufreq_of_match[] = {268	{ .compatible = "nvidia,tegra186-ccplex-cluster", },269	{ }270};271MODULE_DEVICE_TABLE(of, tegra186_cpufreq_of_match);272 273static struct platform_driver tegra186_cpufreq_platform_driver = {274	.driver = {275		.name = "tegra186-cpufreq",276		.of_match_table = tegra186_cpufreq_of_match,277	},278	.probe = tegra186_cpufreq_probe,279	.remove_new = tegra186_cpufreq_remove,280};281module_platform_driver(tegra186_cpufreq_platform_driver);282 283MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");284MODULE_DESCRIPTION("NVIDIA Tegra186 cpufreq driver");285MODULE_LICENSE("GPL v2");286