162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CPU frequency scaling for DaVinci
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on linux/arch/arm/plat-omap/cpu-omap.c. Original Copyright follows:
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  Copyright (C) 2005 Nokia Corporation
1062306a36Sopenharmony_ci *  Written by Tony Lindgren <tony@atomide.com>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Copyright (C) 2007-2008 Texas Instruments, Inc.
1562306a36Sopenharmony_ci * Updated to support OMAP3
1662306a36Sopenharmony_ci * Rajendra Nayak <rnayak@ti.com>
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci#include <linux/types.h>
1962306a36Sopenharmony_ci#include <linux/cpufreq.h>
2062306a36Sopenharmony_ci#include <linux/init.h>
2162306a36Sopenharmony_ci#include <linux/err.h>
2262306a36Sopenharmony_ci#include <linux/clk.h>
2362306a36Sopenharmony_ci#include <linux/platform_data/davinci-cpufreq.h>
2462306a36Sopenharmony_ci#include <linux/platform_device.h>
2562306a36Sopenharmony_ci#include <linux/export.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct davinci_cpufreq {
2862306a36Sopenharmony_ci	struct device *dev;
2962306a36Sopenharmony_ci	struct clk *armclk;
3062306a36Sopenharmony_ci	struct clk *asyncclk;
3162306a36Sopenharmony_ci	unsigned long asyncrate;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_cistatic struct davinci_cpufreq cpufreq;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int davinci_target(struct cpufreq_policy *policy, unsigned int idx)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
3862306a36Sopenharmony_ci	struct clk *armclk = cpufreq.armclk;
3962306a36Sopenharmony_ci	unsigned int old_freq, new_freq;
4062306a36Sopenharmony_ci	int ret = 0;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	old_freq = policy->cur;
4362306a36Sopenharmony_ci	new_freq = pdata->freq_table[idx].frequency;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* if moving to higher frequency, up the voltage beforehand */
4662306a36Sopenharmony_ci	if (pdata->set_voltage && new_freq > old_freq) {
4762306a36Sopenharmony_ci		ret = pdata->set_voltage(idx);
4862306a36Sopenharmony_ci		if (ret)
4962306a36Sopenharmony_ci			return ret;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	ret = clk_set_rate(armclk, new_freq * 1000);
5362306a36Sopenharmony_ci	if (ret)
5462306a36Sopenharmony_ci		return ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (cpufreq.asyncclk) {
5762306a36Sopenharmony_ci		ret = clk_set_rate(cpufreq.asyncclk, cpufreq.asyncrate);
5862306a36Sopenharmony_ci		if (ret)
5962306a36Sopenharmony_ci			return ret;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* if moving to lower freq, lower the voltage after lowering freq */
6362306a36Sopenharmony_ci	if (pdata->set_voltage && new_freq < old_freq)
6462306a36Sopenharmony_ci		pdata->set_voltage(idx);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int davinci_cpu_init(struct cpufreq_policy *policy)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	int result = 0;
7262306a36Sopenharmony_ci	struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
7362306a36Sopenharmony_ci	struct cpufreq_frequency_table *freq_table = pdata->freq_table;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (policy->cpu != 0)
7662306a36Sopenharmony_ci		return -EINVAL;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* Finish platform specific initialization */
7962306a36Sopenharmony_ci	if (pdata->init) {
8062306a36Sopenharmony_ci		result = pdata->init();
8162306a36Sopenharmony_ci		if (result)
8262306a36Sopenharmony_ci			return result;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	policy->clk = cpufreq.armclk;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/*
8862306a36Sopenharmony_ci	 * Time measurement across the target() function yields ~1500-1800us
8962306a36Sopenharmony_ci	 * time taken with no drivers on notification list.
9062306a36Sopenharmony_ci	 * Setting the latency to 2000 us to accommodate addition of drivers
9162306a36Sopenharmony_ci	 * to pre/post change notification list.
9262306a36Sopenharmony_ci	 */
9362306a36Sopenharmony_ci	cpufreq_generic_init(policy, freq_table, 2000 * 1000);
9462306a36Sopenharmony_ci	return 0;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic struct cpufreq_driver davinci_driver = {
9862306a36Sopenharmony_ci	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK,
9962306a36Sopenharmony_ci	.verify		= cpufreq_generic_frequency_table_verify,
10062306a36Sopenharmony_ci	.target_index	= davinci_target,
10162306a36Sopenharmony_ci	.get		= cpufreq_generic_get,
10262306a36Sopenharmony_ci	.init		= davinci_cpu_init,
10362306a36Sopenharmony_ci	.name		= "davinci",
10462306a36Sopenharmony_ci	.attr		= cpufreq_generic_attr,
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic int __init davinci_cpufreq_probe(struct platform_device *pdev)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct davinci_cpufreq_config *pdata = pdev->dev.platform_data;
11062306a36Sopenharmony_ci	struct clk *asyncclk;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (!pdata)
11362306a36Sopenharmony_ci		return -EINVAL;
11462306a36Sopenharmony_ci	if (!pdata->freq_table)
11562306a36Sopenharmony_ci		return -EINVAL;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	cpufreq.dev = &pdev->dev;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	cpufreq.armclk = clk_get(NULL, "arm");
12062306a36Sopenharmony_ci	if (IS_ERR(cpufreq.armclk)) {
12162306a36Sopenharmony_ci		dev_err(cpufreq.dev, "Unable to get ARM clock\n");
12262306a36Sopenharmony_ci		return PTR_ERR(cpufreq.armclk);
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	asyncclk = clk_get(cpufreq.dev, "async");
12662306a36Sopenharmony_ci	if (!IS_ERR(asyncclk)) {
12762306a36Sopenharmony_ci		cpufreq.asyncclk = asyncclk;
12862306a36Sopenharmony_ci		cpufreq.asyncrate = clk_get_rate(asyncclk);
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return cpufreq_register_driver(&davinci_driver);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic void __exit davinci_cpufreq_remove(struct platform_device *pdev)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	cpufreq_unregister_driver(&davinci_driver);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	clk_put(cpufreq.armclk);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (cpufreq.asyncclk)
14162306a36Sopenharmony_ci		clk_put(cpufreq.asyncclk);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic struct platform_driver davinci_cpufreq_driver = {
14562306a36Sopenharmony_ci	.driver = {
14662306a36Sopenharmony_ci		.name	 = "cpufreq-davinci",
14762306a36Sopenharmony_ci	},
14862306a36Sopenharmony_ci	.remove_new = __exit_p(davinci_cpufreq_remove),
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ciint __init davinci_cpufreq_init(void)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	return platform_driver_probe(&davinci_cpufreq_driver,
15462306a36Sopenharmony_ci							davinci_cpufreq_probe);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
157