^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * CPU frequency scaling for OMAP using OPP information
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2005 Nokia Corporation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Written by Tony Lindgren <tony@atomide.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Copyright (C) 2007-2011 Texas Instruments, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * - OMAP3/4 support by Rajendra Nayak, Santosh Shilimkar
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/cpufreq.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/pm_opp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <linux/cpu.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #include <linux/regulator/consumer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #include <asm/smp_plat.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #include <asm/cpu.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) /* OPP tolerance in percentage */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #define OPP_TOLERANCE 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static struct cpufreq_frequency_table *freq_table;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) static atomic_t freq_table_users = ATOMIC_INIT(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) static struct device *mpu_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) static struct regulator *mpu_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static int omap_target(struct cpufreq_policy *policy, unsigned int index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) int r, ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) struct dev_pm_opp *opp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) unsigned long freq, volt = 0, volt_old = 0, tol = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) unsigned int old_freq, new_freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) old_freq = policy->cur;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) new_freq = freq_table[index].frequency;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) freq = new_freq * 1000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) ret = clk_round_rate(policy->clk, freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) if (ret < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) dev_warn(mpu_dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) "CPUfreq: Cannot find matching frequency for %lu\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) freq = ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) if (mpu_reg) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) opp = dev_pm_opp_find_freq_ceil(mpu_dev, &freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) if (IS_ERR(opp)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) __func__, new_freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) volt = dev_pm_opp_get_voltage(opp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) dev_pm_opp_put(opp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) tol = volt * OPP_TOLERANCE / 100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) volt_old = regulator_get_voltage(mpu_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) dev_dbg(mpu_dev, "cpufreq-omap: %u MHz, %ld mV --> %u MHz, %ld mV\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) old_freq / 1000, volt_old ? volt_old / 1000 : -1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) new_freq / 1000, volt ? volt / 1000 : -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) /* scaling up? scale voltage before frequency */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) if (mpu_reg && (new_freq > old_freq)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) if (r < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) dev_warn(mpu_dev, "%s: unable to scale voltage up.\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) return r;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) ret = clk_set_rate(policy->clk, new_freq * 1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) /* scaling down? scale voltage after frequency */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) if (mpu_reg && (new_freq < old_freq)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) if (r < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) dev_warn(mpu_dev, "%s: unable to scale voltage down.\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) clk_set_rate(policy->clk, old_freq * 1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return r;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) static inline void freq_table_free(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) if (atomic_dec_and_test(&freq_table_users))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) dev_pm_opp_free_cpufreq_table(mpu_dev, &freq_table);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) static int omap_cpu_init(struct cpufreq_policy *policy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) int result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) policy->clk = clk_get(NULL, "cpufreq_ck");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) if (IS_ERR(policy->clk))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) return PTR_ERR(policy->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) if (!freq_table) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) result = dev_pm_opp_init_cpufreq_table(mpu_dev, &freq_table);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) if (result) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) dev_err(mpu_dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) "%s: cpu%d: failed creating freq table[%d]\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) __func__, policy->cpu, result);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) clk_put(policy->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) return result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) atomic_inc_return(&freq_table_users);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) /* FIXME: what's the actual transition time? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) cpufreq_generic_init(policy, freq_table, 300 * 1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) dev_pm_opp_of_register_em(mpu_dev, policy->cpus);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) static int omap_cpu_exit(struct cpufreq_policy *policy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) freq_table_free();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) clk_put(policy->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) static struct cpufreq_driver omap_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) .verify = cpufreq_generic_frequency_table_verify,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) .target_index = omap_target,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) .get = cpufreq_generic_get,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) .init = omap_cpu_init,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) .exit = omap_cpu_exit,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) .name = "omap",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) .attr = cpufreq_generic_attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) static int omap_cpufreq_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) mpu_dev = get_cpu_device(0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) if (!mpu_dev) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) pr_warn("%s: unable to get the MPU device\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) mpu_reg = regulator_get(mpu_dev, "vcc");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) if (IS_ERR(mpu_reg)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) pr_warn("%s: unable to get MPU regulator\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) mpu_reg = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) * Ensure physical regulator is present.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) * (e.g. could be dummy regulator.)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) if (regulator_get_voltage(mpu_reg) < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) pr_warn("%s: physical regulator not present for MPU\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) regulator_put(mpu_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) mpu_reg = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) return cpufreq_register_driver(&omap_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) static int omap_cpufreq_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) return cpufreq_unregister_driver(&omap_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) static struct platform_driver omap_cpufreq_platdrv = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) .name = "omap-cpufreq",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) .probe = omap_cpufreq_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) .remove = omap_cpufreq_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) module_platform_driver(omap_cpufreq_platdrv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) MODULE_LICENSE("GPL");