linux/drivers/devfreq/tegra20-devfreq.c
<<
>>
Prefs
   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