xref: /linux/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c (revision 2690e912644e610854c4c3b23d0a0daec9d030ca)
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>
2362a8a094STuomas Tynkkynen #include <linux/module.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 } },
75*2690e912SThierry 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 	platform_set_drvdata(pdev, soc);
129f7c42d98SThierry Reding 
130f7c42d98SThierry Reding 	return 0;
131f7c42d98SThierry Reding }
132f7c42d98SThierry Reding 
133f7c42d98SThierry Reding static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
134f7c42d98SThierry Reding {
135f7c42d98SThierry Reding 	struct tegra_dfll_soc_data *soc = platform_get_drvdata(pdev);
136f7c42d98SThierry Reding 	int err;
137f7c42d98SThierry Reding 
138f7c42d98SThierry Reding 	err = tegra_dfll_unregister(pdev);
139f7c42d98SThierry Reding 	if (err < 0)
140f7c42d98SThierry Reding 		dev_err(&pdev->dev, "failed to unregister DFLL: %d\n", err);
141f7c42d98SThierry Reding 
142f7c42d98SThierry Reding 	tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq);
143f7c42d98SThierry Reding 
144f7c42d98SThierry Reding 	return 0;
14562a8a094STuomas Tynkkynen }
14662a8a094STuomas Tynkkynen 
14762a8a094STuomas Tynkkynen static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
14862a8a094STuomas Tynkkynen 	{ .compatible = "nvidia,tegra124-dfll", },
14962a8a094STuomas Tynkkynen 	{ },
15062a8a094STuomas Tynkkynen };
15162a8a094STuomas Tynkkynen MODULE_DEVICE_TABLE(of, tegra124_dfll_fcpu_of_match);
15262a8a094STuomas Tynkkynen 
15362a8a094STuomas Tynkkynen static const struct dev_pm_ops tegra124_dfll_pm_ops = {
15462a8a094STuomas Tynkkynen 	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
15562a8a094STuomas Tynkkynen 			   tegra_dfll_runtime_resume, NULL)
15662a8a094STuomas Tynkkynen };
15762a8a094STuomas Tynkkynen 
15862a8a094STuomas Tynkkynen static struct platform_driver tegra124_dfll_fcpu_driver = {
15962a8a094STuomas Tynkkynen 	.probe = tegra124_dfll_fcpu_probe,
160f7c42d98SThierry Reding 	.remove = tegra124_dfll_fcpu_remove,
16162a8a094STuomas Tynkkynen 	.driver = {
16262a8a094STuomas Tynkkynen 		.name = "tegra124-dfll",
16362a8a094STuomas Tynkkynen 		.of_match_table = tegra124_dfll_fcpu_of_match,
16462a8a094STuomas Tynkkynen 		.pm = &tegra124_dfll_pm_ops,
16562a8a094STuomas Tynkkynen 	},
16662a8a094STuomas Tynkkynen };
16762a8a094STuomas Tynkkynen 
16862a8a094STuomas Tynkkynen static int __init tegra124_dfll_fcpu_init(void)
16962a8a094STuomas Tynkkynen {
17062a8a094STuomas Tynkkynen 	return platform_driver_register(&tegra124_dfll_fcpu_driver);
17162a8a094STuomas Tynkkynen }
17262a8a094STuomas Tynkkynen module_init(tegra124_dfll_fcpu_init);
17362a8a094STuomas Tynkkynen 
17462a8a094STuomas Tynkkynen static void __exit tegra124_dfll_fcpu_exit(void)
17562a8a094STuomas Tynkkynen {
17662a8a094STuomas Tynkkynen 	platform_driver_unregister(&tegra124_dfll_fcpu_driver);
17762a8a094STuomas Tynkkynen }
17862a8a094STuomas Tynkkynen module_exit(tegra124_dfll_fcpu_exit);
17962a8a094STuomas Tynkkynen 
18062a8a094STuomas Tynkkynen MODULE_DESCRIPTION("Tegra124 DFLL clock source driver");
18162a8a094STuomas Tynkkynen MODULE_LICENSE("GPL v2");
18262a8a094STuomas Tynkkynen MODULE_AUTHOR("Aleksandr Frid <afrid@nvidia.com>");
18362a8a094STuomas Tynkkynen MODULE_AUTHOR("Paul Walmsley <pwalmsley@nvidia.com>");
184