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