1// SPDX-License-Identifier: GPL-2.0 2/* 3 * NVIDIA Tegra20 devfreq driver 4 * 5 * Copyright (C) 2019 GRATE-DRIVER project 6 */ 7 8#include <linux/clk.h> 9#include <linux/devfreq.h> 10#include <linux/io.h> 11#include <linux/kernel.h> 12#include <linux/module.h> 13#include <linux/of_device.h> 14#include <linux/platform_device.h> 15#include <linux/pm_opp.h> 16#include <linux/slab.h> 17 18#include <soc/tegra/mc.h> 19 20#include "governor.h" 21 22#define MC_STAT_CONTROL 0x90 23#define MC_STAT_EMC_CLOCK_LIMIT 0xa0 24#define MC_STAT_EMC_CLOCKS 0xa4 25#define MC_STAT_EMC_CONTROL 0xa8 26#define MC_STAT_EMC_COUNT 0xb8 27 28#define EMC_GATHER_CLEAR (1 << 8) 29#define EMC_GATHER_ENABLE (3 << 8) 30 31struct tegra_devfreq { 32 struct devfreq *devfreq; 33 struct clk *emc_clock; 34 void __iomem *regs; 35}; 36 37static int tegra_devfreq_target(struct device *dev, unsigned long *freq, 38 u32 flags) 39{ 40 struct tegra_devfreq *tegra = dev_get_drvdata(dev); 41 struct devfreq *devfreq = tegra->devfreq; 42 struct dev_pm_opp *opp; 43 unsigned long rate; 44 int err; 45 46 opp = devfreq_recommended_opp(dev, freq, flags); 47 if (IS_ERR(opp)) 48 return PTR_ERR(opp); 49 50 rate = dev_pm_opp_get_freq(opp); 51 dev_pm_opp_put(opp); 52 53 err = clk_set_min_rate(tegra->emc_clock, rate); 54 if (err) 55 return err; 56 57 err = clk_set_rate(tegra->emc_clock, 0); 58 if (err) 59 goto restore_min_rate; 60 61 return 0; 62 63restore_min_rate: 64 clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq); 65 66 return err; 67} 68 69static int tegra_devfreq_get_dev_status(struct device *dev, 70 struct devfreq_dev_status *stat) 71{ 72 struct tegra_devfreq *tegra = dev_get_drvdata(dev); 73 74 /* 75 * EMC_COUNT returns number of memory events, that number is lower 76 * than the number of clocks. Conversion ratio of 1/8 results in a 77 * bit higher bandwidth than actually needed, it is good enough for 78 * the time being because drivers don't support requesting minimum 79 * needed memory bandwidth yet. 80 * 81 * TODO: adjust the ratio value once relevant drivers will support 82 * memory bandwidth management. 83 */ 84 stat->busy_time = readl_relaxed(tegra->regs + MC_STAT_EMC_COUNT); 85 stat->total_time = readl_relaxed(tegra->regs + MC_STAT_EMC_CLOCKS) / 8; 86 stat->current_frequency = clk_get_rate(tegra->emc_clock); 87 88 writel_relaxed(EMC_GATHER_CLEAR, tegra->regs + MC_STAT_CONTROL); 89 writel_relaxed(EMC_GATHER_ENABLE, tegra->regs + MC_STAT_CONTROL); 90 91 return 0; 92} 93 94static struct devfreq_dev_profile tegra_devfreq_profile = { 95 .polling_ms = 500, 96 .target = tegra_devfreq_target, 97 .get_dev_status = tegra_devfreq_get_dev_status, 98}; 99 100static struct tegra_mc *tegra_get_memory_controller(void) 101{ 102 struct platform_device *pdev; 103 struct device_node *np; 104 struct tegra_mc *mc; 105 106 np = of_find_compatible_node(NULL, NULL, "nvidia,tegra20-mc-gart"); 107 if (!np) 108 return ERR_PTR(-ENOENT); 109 110 pdev = of_find_device_by_node(np); 111 of_node_put(np); 112 if (!pdev) 113 return ERR_PTR(-ENODEV); 114 115 mc = platform_get_drvdata(pdev); 116 if (!mc) 117 return ERR_PTR(-EPROBE_DEFER); 118 119 return mc; 120} 121 122static int tegra_devfreq_probe(struct platform_device *pdev) 123{ 124 struct tegra_devfreq *tegra; 125 struct tegra_mc *mc; 126 unsigned long max_rate; 127 unsigned long rate; 128 int err; 129 130 mc = tegra_get_memory_controller(); 131 if (IS_ERR(mc)) { 132 err = PTR_ERR(mc); 133 dev_err(&pdev->dev, "failed to get memory controller: %d\n", 134 err); 135 return err; 136 } 137 138 tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); 139 if (!tegra) 140 return -ENOMEM; 141 142 /* EMC is a system-critical clock that is always enabled */ 143 tegra->emc_clock = devm_clk_get(&pdev->dev, "emc"); 144 if (IS_ERR(tegra->emc_clock)) { 145 err = PTR_ERR(tegra->emc_clock); 146 dev_err(&pdev->dev, "failed to get emc clock: %d\n", err); 147 return err; 148 } 149 150 tegra->regs = mc->regs; 151 152 max_rate = clk_round_rate(tegra->emc_clock, ULONG_MAX); 153 154 for (rate = 0; rate <= max_rate; rate++) { 155 rate = clk_round_rate(tegra->emc_clock, rate); 156 157 err = dev_pm_opp_add(&pdev->dev, rate, 0); 158 if (err) { 159 dev_err(&pdev->dev, "failed to add opp: %d\n", err); 160 goto remove_opps; 161 } 162 } 163 164 /* 165 * Reset statistic gathers state, select global bandwidth for the 166 * statistics collection mode and set clocks counter saturation 167 * limit to maximum. 168 */ 169 writel_relaxed(0x00000000, tegra->regs + MC_STAT_CONTROL); 170 writel_relaxed(0x00000000, tegra->regs + MC_STAT_EMC_CONTROL); 171 writel_relaxed(0xffffffff, tegra->regs + MC_STAT_EMC_CLOCK_LIMIT); 172 173 platform_set_drvdata(pdev, tegra); 174 175 tegra->devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile, 176 DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL); 177 if (IS_ERR(tegra->devfreq)) { 178 err = PTR_ERR(tegra->devfreq); 179 goto remove_opps; 180 } 181 182 return 0; 183 184remove_opps: 185 dev_pm_opp_remove_all_dynamic(&pdev->dev); 186 187 return err; 188} 189 190static int tegra_devfreq_remove(struct platform_device *pdev) 191{ 192 struct tegra_devfreq *tegra = platform_get_drvdata(pdev); 193 194 devfreq_remove_device(tegra->devfreq); 195 dev_pm_opp_remove_all_dynamic(&pdev->dev); 196 197 return 0; 198} 199 200static struct platform_driver tegra_devfreq_driver = { 201 .probe = tegra_devfreq_probe, 202 .remove = tegra_devfreq_remove, 203 .driver = { 204 .name = "tegra20-devfreq", 205 }, 206}; 207module_platform_driver(tegra_devfreq_driver); 208 209MODULE_ALIAS("platform:tegra20-devfreq"); 210MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>"); 211MODULE_DESCRIPTION("NVIDIA Tegra20 devfreq driver"); 212MODULE_LICENSE("GPL v2"); 213