xref: /linux/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c (revision 1752c9ee23fb20e5bfdbedf677e91f927f2b8d80)
162a8a094STuomas Tynkkynen /*
262a8a094STuomas Tynkkynen  * Tegra124 DFLL FCPU clock source driver
362a8a094STuomas Tynkkynen  *
462a8a094STuomas Tynkkynen  * Copyright (C) 2012-2014 NVIDIA Corporation.  All rights reserved.
562a8a094STuomas Tynkkynen  *
662a8a094STuomas Tynkkynen  * Aleksandr Frid <afrid@nvidia.com>
762a8a094STuomas Tynkkynen  * Paul Walmsley <pwalmsley@nvidia.com>
862a8a094STuomas Tynkkynen  *
962a8a094STuomas Tynkkynen  * This program is free software; you can redistribute it and/or modify
1062a8a094STuomas Tynkkynen  * it under the terms of the GNU General Public License version 2 as
1162a8a094STuomas Tynkkynen  * published by the Free Software Foundation.
1262a8a094STuomas Tynkkynen  *
1362a8a094STuomas Tynkkynen  * This program is distributed in the hope that it will be useful, but WITHOUT
1462a8a094STuomas Tynkkynen  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1562a8a094STuomas Tynkkynen  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1662a8a094STuomas Tynkkynen  * more details.
1762a8a094STuomas Tynkkynen  *
1862a8a094STuomas Tynkkynen  */
1962a8a094STuomas Tynkkynen 
2062a8a094STuomas Tynkkynen #include <linux/cpu.h>
2162a8a094STuomas Tynkkynen #include <linux/err.h>
2262a8a094STuomas Tynkkynen #include <linux/kernel.h>
2333996b02SPaul Gortmaker #include <linux/init.h>
2462a8a094STuomas Tynkkynen #include <linux/platform_device.h>
2562a8a094STuomas Tynkkynen #include <soc/tegra/fuse.h>
2662a8a094STuomas Tynkkynen 
2762a8a094STuomas Tynkkynen #include "clk.h"
2862a8a094STuomas Tynkkynen #include "clk-dfll.h"
2962a8a094STuomas Tynkkynen #include "cvb.h"
3062a8a094STuomas Tynkkynen 
3162a8a094STuomas Tynkkynen /* Maximum CPU frequency, indexed by CPU speedo id */
3262a8a094STuomas Tynkkynen static const unsigned long cpu_max_freq_table[] = {
3362a8a094STuomas Tynkkynen 	[0] = 2014500000UL,
3462a8a094STuomas Tynkkynen 	[1] = 2320500000UL,
3562a8a094STuomas Tynkkynen 	[2] = 2116500000UL,
3662a8a094STuomas Tynkkynen 	[3] = 2524500000UL,
3762a8a094STuomas Tynkkynen };
3862a8a094STuomas Tynkkynen 
3962a8a094STuomas Tynkkynen static const struct cvb_table tegra124_cpu_cvb_tables[] = {
4062a8a094STuomas Tynkkynen 	{
4162a8a094STuomas Tynkkynen 		.speedo_id = -1,
4262a8a094STuomas Tynkkynen 		.process_id = -1,
4362a8a094STuomas Tynkkynen 		.min_millivolts = 900,
4462a8a094STuomas Tynkkynen 		.max_millivolts = 1260,
4562a8a094STuomas Tynkkynen 		.alignment = {
4662a8a094STuomas Tynkkynen 			.step_uv = 10000, /* 10mV */
4762a8a094STuomas Tynkkynen 		},
4862a8a094STuomas Tynkkynen 		.speedo_scale = 100,
4962a8a094STuomas Tynkkynen 		.voltage_scale = 1000,
50e8f6a68cSThierry Reding 		.entries = {
5162a8a094STuomas Tynkkynen 			{  204000000UL, { 1112619, -29295, 402 } },
5262a8a094STuomas Tynkkynen 			{  306000000UL, { 1150460, -30585, 402 } },
5362a8a094STuomas Tynkkynen 			{  408000000UL, { 1190122, -31865, 402 } },
5462a8a094STuomas Tynkkynen 			{  510000000UL, { 1231606, -33155, 402 } },
5562a8a094STuomas Tynkkynen 			{  612000000UL, { 1274912, -34435, 402 } },
5662a8a094STuomas Tynkkynen 			{  714000000UL, { 1320040, -35725, 402 } },
5762a8a094STuomas Tynkkynen 			{  816000000UL, { 1366990, -37005, 402 } },
5862a8a094STuomas Tynkkynen 			{  918000000UL, { 1415762, -38295, 402 } },
5962a8a094STuomas Tynkkynen 			{ 1020000000UL, { 1466355, -39575, 402 } },
6062a8a094STuomas Tynkkynen 			{ 1122000000UL, { 1518771, -40865, 402 } },
6162a8a094STuomas Tynkkynen 			{ 1224000000UL, { 1573009, -42145, 402 } },
6262a8a094STuomas Tynkkynen 			{ 1326000000UL, { 1629068, -43435, 402 } },
6362a8a094STuomas Tynkkynen 			{ 1428000000UL, { 1686950, -44715, 402 } },
6462a8a094STuomas Tynkkynen 			{ 1530000000UL, { 1746653, -46005, 402 } },
6562a8a094STuomas Tynkkynen 			{ 1632000000UL, { 1808179, -47285, 402 } },
6662a8a094STuomas Tynkkynen 			{ 1734000000UL, { 1871526, -48575, 402 } },
6762a8a094STuomas Tynkkynen 			{ 1836000000UL, { 1936696, -49855, 402 } },
6862a8a094STuomas Tynkkynen 			{ 1938000000UL, { 2003687, -51145, 402 } },
6962a8a094STuomas Tynkkynen 			{ 2014500000UL, { 2054787, -52095, 402 } },
7062a8a094STuomas Tynkkynen 			{ 2116500000UL, { 2124957, -53385, 402 } },
7162a8a094STuomas Tynkkynen 			{ 2218500000UL, { 2196950, -54665, 402 } },
7262a8a094STuomas Tynkkynen 			{ 2320500000UL, { 2270765, -55955, 402 } },
7362a8a094STuomas Tynkkynen 			{ 2422500000UL, { 2346401, -57235, 402 } },
7462a8a094STuomas Tynkkynen 			{ 2524500000UL, { 2437299, -58535, 402 } },
752690e912SThierry Reding 			{          0UL, {       0,      0,   0 } },
7662a8a094STuomas Tynkkynen 		},
7762a8a094STuomas Tynkkynen 		.cpu_dfll_data = {
7862a8a094STuomas Tynkkynen 			.tune0_low = 0x005020ff,
7962a8a094STuomas Tynkkynen 			.tune0_high = 0x005040ff,
8062a8a094STuomas Tynkkynen 			.tune1 = 0x00000060,
8162a8a094STuomas Tynkkynen 		}
8262a8a094STuomas Tynkkynen 	},
8362a8a094STuomas Tynkkynen };
8462a8a094STuomas Tynkkynen 
8562a8a094STuomas Tynkkynen static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
8662a8a094STuomas Tynkkynen {
87f7c42d98SThierry Reding 	int process_id, speedo_id, speedo_value, err;
8862a8a094STuomas Tynkkynen 	struct tegra_dfll_soc_data *soc;
8962a8a094STuomas Tynkkynen 
9062a8a094STuomas Tynkkynen 	process_id = tegra_sku_info.cpu_process_id;
9162a8a094STuomas Tynkkynen 	speedo_id = tegra_sku_info.cpu_speedo_id;
9262a8a094STuomas Tynkkynen 	speedo_value = tegra_sku_info.cpu_speedo_value;
9362a8a094STuomas Tynkkynen 
9462a8a094STuomas Tynkkynen 	if (speedo_id >= ARRAY_SIZE(cpu_max_freq_table)) {
9562a8a094STuomas Tynkkynen 		dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n",
9662a8a094STuomas Tynkkynen 			speedo_id);
9762a8a094STuomas Tynkkynen 		return -ENODEV;
9862a8a094STuomas Tynkkynen 	}
9962a8a094STuomas Tynkkynen 
10062a8a094STuomas Tynkkynen 	soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL);
10162a8a094STuomas Tynkkynen 	if (!soc)
10262a8a094STuomas Tynkkynen 		return -ENOMEM;
10362a8a094STuomas Tynkkynen 
10462a8a094STuomas Tynkkynen 	soc->dev = get_cpu_device(0);
10562a8a094STuomas Tynkkynen 	if (!soc->dev) {
10662a8a094STuomas Tynkkynen 		dev_err(&pdev->dev, "no CPU0 device\n");
10762a8a094STuomas Tynkkynen 		return -ENODEV;
10862a8a094STuomas Tynkkynen 	}
10962a8a094STuomas Tynkkynen 
110f7c42d98SThierry Reding 	soc->max_freq = cpu_max_freq_table[speedo_id];
111f7c42d98SThierry Reding 
112e8f6a68cSThierry Reding 	soc->cvb = tegra_cvb_add_opp_table(soc->dev, tegra124_cpu_cvb_tables,
11362a8a094STuomas Tynkkynen 					   ARRAY_SIZE(tegra124_cpu_cvb_tables),
11462a8a094STuomas Tynkkynen 					   process_id, speedo_id, speedo_value,
115f7c42d98SThierry Reding 					   soc->max_freq);
11627ed2f7eSThierry Reding 	if (IS_ERR(soc->cvb)) {
11727ed2f7eSThierry Reding 		dev_err(&pdev->dev, "couldn't add OPP table: %ld\n",
11827ed2f7eSThierry Reding 			PTR_ERR(soc->cvb));
11927ed2f7eSThierry Reding 		return PTR_ERR(soc->cvb);
12062a8a094STuomas Tynkkynen 	}
12162a8a094STuomas Tynkkynen 
122f7c42d98SThierry Reding 	err = tegra_dfll_register(pdev, soc);
123f7c42d98SThierry Reding 	if (err < 0) {
124f7c42d98SThierry Reding 		tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq);
125f7c42d98SThierry Reding 		return err;
126f7c42d98SThierry Reding 	}
12762a8a094STuomas Tynkkynen 
128f7c42d98SThierry Reding 	return 0;
129f7c42d98SThierry Reding }
130f7c42d98SThierry Reding 
131f7c42d98SThierry Reding static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
132f7c42d98SThierry Reding {
133*1752c9eeSNicolin Chen 	struct tegra_dfll_soc_data *soc;
134f7c42d98SThierry Reding 
135*1752c9eeSNicolin Chen 	soc = tegra_dfll_unregister(pdev);
136*1752c9eeSNicolin Chen 	if (IS_ERR(soc))
137*1752c9eeSNicolin Chen 		dev_err(&pdev->dev, "failed to unregister DFLL: %ld\n",
138*1752c9eeSNicolin Chen 			PTR_ERR(soc));
139f7c42d98SThierry Reding 
140f7c42d98SThierry Reding 	tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq);
141f7c42d98SThierry Reding 
142f7c42d98SThierry Reding 	return 0;
14362a8a094STuomas Tynkkynen }
14462a8a094STuomas Tynkkynen 
14562a8a094STuomas Tynkkynen static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
14662a8a094STuomas Tynkkynen 	{ .compatible = "nvidia,tegra124-dfll", },
14762a8a094STuomas Tynkkynen 	{ },
14862a8a094STuomas Tynkkynen };
14962a8a094STuomas Tynkkynen 
15062a8a094STuomas Tynkkynen static const struct dev_pm_ops tegra124_dfll_pm_ops = {
15162a8a094STuomas Tynkkynen 	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
15262a8a094STuomas Tynkkynen 			   tegra_dfll_runtime_resume, NULL)
15362a8a094STuomas Tynkkynen };
15462a8a094STuomas Tynkkynen 
15562a8a094STuomas Tynkkynen static struct platform_driver tegra124_dfll_fcpu_driver = {
15662a8a094STuomas Tynkkynen 	.probe = tegra124_dfll_fcpu_probe,
157f7c42d98SThierry Reding 	.remove = tegra124_dfll_fcpu_remove,
15862a8a094STuomas Tynkkynen 	.driver = {
15962a8a094STuomas Tynkkynen 		.name = "tegra124-dfll",
16062a8a094STuomas Tynkkynen 		.of_match_table = tegra124_dfll_fcpu_of_match,
16162a8a094STuomas Tynkkynen 		.pm = &tegra124_dfll_pm_ops,
16262a8a094STuomas Tynkkynen 	},
16362a8a094STuomas Tynkkynen };
1646f877e79SWei Yongjun builtin_platform_driver(tegra124_dfll_fcpu_driver);
165