linux/drivers/cpufreq/davinci-cpufreq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * CPU frequency scaling for DaVinci
   4 *
   5 * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/
   6 *
   7 * Based on linux/arch/arm/plat-omap/cpu-omap.c. Original Copyright follows:
   8 *
   9 *  Copyright (C) 2005 Nokia Corporation
  10 *  Written by Tony Lindgren <tony@atomide.com>
  11 *
  12 *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
  13 *
  14 * Copyright (C) 2007-2008 Texas Instruments, Inc.
  15 * Updated to support OMAP3
  16 * Rajendra Nayak <rnayak@ti.com>
  17 */
  18#include <linux/types.h>
  19#include <linux/cpufreq.h>
  20#include <linux/init.h>
  21#include <linux/err.h>
  22#include <linux/clk.h>
  23#include <linux/platform_data/davinci-cpufreq.h>
  24#include <linux/platform_device.h>
  25#include <linux/export.h>
  26
  27struct davinci_cpufreq {
  28        struct device *dev;
  29        struct clk *armclk;
  30        struct clk *asyncclk;
  31        unsigned long asyncrate;
  32};
  33static struct davinci_cpufreq cpufreq;
  34
  35static int davinci_target(struct cpufreq_policy *policy, unsigned int idx)
  36{
  37        struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
  38        struct clk *armclk = cpufreq.armclk;
  39        unsigned int old_freq, new_freq;
  40        int ret = 0;
  41
  42        old_freq = policy->cur;
  43        new_freq = pdata->freq_table[idx].frequency;
  44
  45        /* if moving to higher frequency, up the voltage beforehand */
  46        if (pdata->set_voltage && new_freq > old_freq) {
  47                ret = pdata->set_voltage(idx);
  48                if (ret)
  49                        return ret;
  50        }
  51
  52        ret = clk_set_rate(armclk, new_freq * 1000);
  53        if (ret)
  54                return ret;
  55
  56        if (cpufreq.asyncclk) {
  57                ret = clk_set_rate(cpufreq.asyncclk, cpufreq.asyncrate);
  58                if (ret)
  59                        return ret;
  60        }
  61
  62        /* if moving to lower freq, lower the voltage after lowering freq */
  63        if (pdata->set_voltage && new_freq < old_freq)
  64                pdata->set_voltage(idx);
  65
  66        return 0;
  67}
  68
  69static int davinci_cpu_init(struct cpufreq_policy *policy)
  70{
  71        int result = 0;
  72        struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
  73        struct cpufreq_frequency_table *freq_table = pdata->freq_table;
  74
  75        if (policy->cpu != 0)
  76                return -EINVAL;
  77
  78        /* Finish platform specific initialization */
  79        if (pdata->init) {
  80                result = pdata->init();
  81                if (result)
  82                        return result;
  83        }
  84
  85        policy->clk = cpufreq.armclk;
  86
  87        /*
  88         * Time measurement across the target() function yields ~1500-1800us
  89         * time taken with no drivers on notification list.
  90         * Setting the latency to 2000 us to accommodate addition of drivers
  91         * to pre/post change notification list.
  92         */
  93        cpufreq_generic_init(policy, freq_table, 2000 * 1000);
  94        return 0;
  95}
  96
  97static struct cpufreq_driver davinci_driver = {
  98        .flags          = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
  99        .verify         = cpufreq_generic_frequency_table_verify,
 100        .target_index   = davinci_target,
 101        .get            = cpufreq_generic_get,
 102        .init           = davinci_cpu_init,
 103        .name           = "davinci",
 104        .attr           = cpufreq_generic_attr,
 105};
 106
 107static int __init davinci_cpufreq_probe(struct platform_device *pdev)
 108{
 109        struct davinci_cpufreq_config *pdata = pdev->dev.platform_data;
 110        struct clk *asyncclk;
 111
 112        if (!pdata)
 113                return -EINVAL;
 114        if (!pdata->freq_table)
 115                return -EINVAL;
 116
 117        cpufreq.dev = &pdev->dev;
 118
 119        cpufreq.armclk = clk_get(NULL, "arm");
 120        if (IS_ERR(cpufreq.armclk)) {
 121                dev_err(cpufreq.dev, "Unable to get ARM clock\n");
 122                return PTR_ERR(cpufreq.armclk);
 123        }
 124
 125        asyncclk = clk_get(cpufreq.dev, "async");
 126        if (!IS_ERR(asyncclk)) {
 127                cpufreq.asyncclk = asyncclk;
 128                cpufreq.asyncrate = clk_get_rate(asyncclk);
 129        }
 130
 131        return cpufreq_register_driver(&davinci_driver);
 132}
 133
 134static int __exit davinci_cpufreq_remove(struct platform_device *pdev)
 135{
 136        clk_put(cpufreq.armclk);
 137
 138        if (cpufreq.asyncclk)
 139                clk_put(cpufreq.asyncclk);
 140
 141        return cpufreq_unregister_driver(&davinci_driver);
 142}
 143
 144static struct platform_driver davinci_cpufreq_driver = {
 145        .driver = {
 146                .name    = "cpufreq-davinci",
 147        },
 148        .remove = __exit_p(davinci_cpufreq_remove),
 149};
 150
 151int __init davinci_cpufreq_init(void)
 152{
 153        return platform_driver_probe(&davinci_cpufreq_driver,
 154                                                        davinci_cpufreq_probe);
 155}
 156
 157