tegra186-emc.c (7383B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2019 NVIDIA CORPORATION. All rights reserved. 4 */ 5 6#include <linux/clk.h> 7#include <linux/debugfs.h> 8#include <linux/module.h> 9#include <linux/mod_devicetable.h> 10#include <linux/platform_device.h> 11 12#include <soc/tegra/bpmp.h> 13 14struct tegra186_emc_dvfs { 15 unsigned long latency; 16 unsigned long rate; 17}; 18 19struct tegra186_emc { 20 struct tegra_bpmp *bpmp; 21 struct device *dev; 22 struct clk *clk; 23 24 struct tegra186_emc_dvfs *dvfs; 25 unsigned int num_dvfs; 26 27 struct { 28 struct dentry *root; 29 unsigned long min_rate; 30 unsigned long max_rate; 31 } debugfs; 32}; 33 34/* 35 * debugfs interface 36 * 37 * The memory controller driver exposes some files in debugfs that can be used 38 * to control the EMC frequency. The top-level directory can be found here: 39 * 40 * /sys/kernel/debug/emc 41 * 42 * It contains the following files: 43 * 44 * - available_rates: This file contains a list of valid, space-separated 45 * EMC frequencies. 46 * 47 * - min_rate: Writing a value to this file sets the given frequency as the 48 * floor of the permitted range. If this is higher than the currently 49 * configured EMC frequency, this will cause the frequency to be 50 * increased so that it stays within the valid range. 51 * 52 * - max_rate: Similarily to the min_rate file, writing a value to this file 53 * sets the given frequency as the ceiling of the permitted range. If 54 * the value is lower than the currently configured EMC frequency, this 55 * will cause the frequency to be decreased so that it stays within the 56 * valid range. 57 */ 58 59static bool tegra186_emc_validate_rate(struct tegra186_emc *emc, 60 unsigned long rate) 61{ 62 unsigned int i; 63 64 for (i = 0; i < emc->num_dvfs; i++) 65 if (rate == emc->dvfs[i].rate) 66 return true; 67 68 return false; 69} 70 71static int tegra186_emc_debug_available_rates_show(struct seq_file *s, 72 void *data) 73{ 74 struct tegra186_emc *emc = s->private; 75 const char *prefix = ""; 76 unsigned int i; 77 78 for (i = 0; i < emc->num_dvfs; i++) { 79 seq_printf(s, "%s%lu", prefix, emc->dvfs[i].rate); 80 prefix = " "; 81 } 82 83 seq_puts(s, "\n"); 84 85 return 0; 86} 87 88static int tegra186_emc_debug_available_rates_open(struct inode *inode, 89 struct file *file) 90{ 91 return single_open(file, tegra186_emc_debug_available_rates_show, 92 inode->i_private); 93} 94 95static const struct file_operations tegra186_emc_debug_available_rates_fops = { 96 .open = tegra186_emc_debug_available_rates_open, 97 .read = seq_read, 98 .llseek = seq_lseek, 99 .release = single_release, 100}; 101 102static int tegra186_emc_debug_min_rate_get(void *data, u64 *rate) 103{ 104 struct tegra186_emc *emc = data; 105 106 *rate = emc->debugfs.min_rate; 107 108 return 0; 109} 110 111static int tegra186_emc_debug_min_rate_set(void *data, u64 rate) 112{ 113 struct tegra186_emc *emc = data; 114 int err; 115 116 if (!tegra186_emc_validate_rate(emc, rate)) 117 return -EINVAL; 118 119 err = clk_set_min_rate(emc->clk, rate); 120 if (err < 0) 121 return err; 122 123 emc->debugfs.min_rate = rate; 124 125 return 0; 126} 127 128DEFINE_DEBUGFS_ATTRIBUTE(tegra186_emc_debug_min_rate_fops, 129 tegra186_emc_debug_min_rate_get, 130 tegra186_emc_debug_min_rate_set, "%llu\n"); 131 132static int tegra186_emc_debug_max_rate_get(void *data, u64 *rate) 133{ 134 struct tegra186_emc *emc = data; 135 136 *rate = emc->debugfs.max_rate; 137 138 return 0; 139} 140 141static int tegra186_emc_debug_max_rate_set(void *data, u64 rate) 142{ 143 struct tegra186_emc *emc = data; 144 int err; 145 146 if (!tegra186_emc_validate_rate(emc, rate)) 147 return -EINVAL; 148 149 err = clk_set_max_rate(emc->clk, rate); 150 if (err < 0) 151 return err; 152 153 emc->debugfs.max_rate = rate; 154 155 return 0; 156} 157 158DEFINE_DEBUGFS_ATTRIBUTE(tegra186_emc_debug_max_rate_fops, 159 tegra186_emc_debug_max_rate_get, 160 tegra186_emc_debug_max_rate_set, "%llu\n"); 161 162static int tegra186_emc_probe(struct platform_device *pdev) 163{ 164 struct mrq_emc_dvfs_latency_response response; 165 struct tegra_bpmp_message msg; 166 struct tegra186_emc *emc; 167 unsigned int i; 168 int err; 169 170 emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL); 171 if (!emc) 172 return -ENOMEM; 173 174 emc->bpmp = tegra_bpmp_get(&pdev->dev); 175 if (IS_ERR(emc->bpmp)) 176 return dev_err_probe(&pdev->dev, PTR_ERR(emc->bpmp), "failed to get BPMP\n"); 177 178 emc->clk = devm_clk_get(&pdev->dev, "emc"); 179 if (IS_ERR(emc->clk)) { 180 err = PTR_ERR(emc->clk); 181 dev_err(&pdev->dev, "failed to get EMC clock: %d\n", err); 182 goto put_bpmp; 183 } 184 185 platform_set_drvdata(pdev, emc); 186 emc->dev = &pdev->dev; 187 188 memset(&msg, 0, sizeof(msg)); 189 msg.mrq = MRQ_EMC_DVFS_LATENCY; 190 msg.tx.data = NULL; 191 msg.tx.size = 0; 192 msg.rx.data = &response; 193 msg.rx.size = sizeof(response); 194 195 err = tegra_bpmp_transfer(emc->bpmp, &msg); 196 if (err < 0) { 197 dev_err(&pdev->dev, "failed to EMC DVFS pairs: %d\n", err); 198 goto put_bpmp; 199 } 200 if (msg.rx.ret < 0) { 201 err = -EINVAL; 202 dev_err(&pdev->dev, "EMC DVFS MRQ failed: %d (BPMP error code)\n", msg.rx.ret); 203 goto put_bpmp; 204 } 205 206 emc->debugfs.min_rate = ULONG_MAX; 207 emc->debugfs.max_rate = 0; 208 209 emc->num_dvfs = response.num_pairs; 210 211 emc->dvfs = devm_kmalloc_array(&pdev->dev, emc->num_dvfs, 212 sizeof(*emc->dvfs), GFP_KERNEL); 213 if (!emc->dvfs) { 214 err = -ENOMEM; 215 goto put_bpmp; 216 } 217 218 dev_dbg(&pdev->dev, "%u DVFS pairs:\n", emc->num_dvfs); 219 220 for (i = 0; i < emc->num_dvfs; i++) { 221 emc->dvfs[i].rate = response.pairs[i].freq * 1000; 222 emc->dvfs[i].latency = response.pairs[i].latency; 223 224 if (emc->dvfs[i].rate < emc->debugfs.min_rate) 225 emc->debugfs.min_rate = emc->dvfs[i].rate; 226 227 if (emc->dvfs[i].rate > emc->debugfs.max_rate) 228 emc->debugfs.max_rate = emc->dvfs[i].rate; 229 230 dev_dbg(&pdev->dev, " %2u: %lu Hz -> %lu us\n", i, 231 emc->dvfs[i].rate, emc->dvfs[i].latency); 232 } 233 234 err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate, 235 emc->debugfs.max_rate); 236 if (err < 0) { 237 dev_err(&pdev->dev, 238 "failed to set rate range [%lu-%lu] for %pC\n", 239 emc->debugfs.min_rate, emc->debugfs.max_rate, 240 emc->clk); 241 goto put_bpmp; 242 } 243 244 emc->debugfs.root = debugfs_create_dir("emc", NULL); 245 debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root, 246 emc, &tegra186_emc_debug_available_rates_fops); 247 debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root, 248 emc, &tegra186_emc_debug_min_rate_fops); 249 debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root, 250 emc, &tegra186_emc_debug_max_rate_fops); 251 252 return 0; 253 254put_bpmp: 255 tegra_bpmp_put(emc->bpmp); 256 return err; 257} 258 259static int tegra186_emc_remove(struct platform_device *pdev) 260{ 261 struct tegra186_emc *emc = platform_get_drvdata(pdev); 262 263 debugfs_remove_recursive(emc->debugfs.root); 264 tegra_bpmp_put(emc->bpmp); 265 266 return 0; 267} 268 269static const struct of_device_id tegra186_emc_of_match[] = { 270#if defined(CONFIG_ARCH_TEGRA_186_SOC) 271 { .compatible = "nvidia,tegra186-emc" }, 272#endif 273#if defined(CONFIG_ARCH_TEGRA_194_SOC) 274 { .compatible = "nvidia,tegra194-emc" }, 275#endif 276#if defined(CONFIG_ARCH_TEGRA_234_SOC) 277 { .compatible = "nvidia,tegra234-emc" }, 278#endif 279 { /* sentinel */ } 280}; 281MODULE_DEVICE_TABLE(of, tegra186_emc_of_match); 282 283static struct platform_driver tegra186_emc_driver = { 284 .driver = { 285 .name = "tegra186-emc", 286 .of_match_table = tegra186_emc_of_match, 287 .suppress_bind_attrs = true, 288 }, 289 .probe = tegra186_emc_probe, 290 .remove = tegra186_emc_remove, 291}; 292module_platform_driver(tegra186_emc_driver); 293 294MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); 295MODULE_DESCRIPTION("NVIDIA Tegra186 External Memory Controller driver"); 296MODULE_LICENSE("GPL v2");