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} },
7562a8a094STuomas Tynkkynen 			{0,		{      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 {
8762a8a094STuomas Tynkkynen 	int process_id, speedo_id, speedo_value;
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 
110e8f6a68cSThierry Reding 	soc->cvb = tegra_cvb_add_opp_table(soc->dev, tegra124_cpu_cvb_tables,
11162a8a094STuomas Tynkkynen 					   ARRAY_SIZE(tegra124_cpu_cvb_tables),
11262a8a094STuomas Tynkkynen 					   process_id, speedo_id, speedo_value,
113e8f6a68cSThierry Reding 					   cpu_max_freq_table[speedo_id]);
11427ed2f7eSThierry Reding 	if (IS_ERR(soc->cvb)) {
11527ed2f7eSThierry Reding 		dev_err(&pdev->dev, "couldn't add OPP table: %ld\n",
11627ed2f7eSThierry Reding 			PTR_ERR(soc->cvb));
11727ed2f7eSThierry Reding 		return PTR_ERR(soc->cvb);
11862a8a094STuomas Tynkkynen 	}
11962a8a094STuomas Tynkkynen 
12062a8a094STuomas Tynkkynen 
12162a8a094STuomas Tynkkynen 	return tegra_dfll_register(pdev, soc);
12262a8a094STuomas Tynkkynen }
12362a8a094STuomas Tynkkynen 
12462a8a094STuomas Tynkkynen static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
12562a8a094STuomas Tynkkynen 	{ .compatible = "nvidia,tegra124-dfll", },
12662a8a094STuomas Tynkkynen 	{ },
12762a8a094STuomas Tynkkynen };
12862a8a094STuomas Tynkkynen MODULE_DEVICE_TABLE(of, tegra124_dfll_fcpu_of_match);
12962a8a094STuomas Tynkkynen 
13062a8a094STuomas Tynkkynen static const struct dev_pm_ops tegra124_dfll_pm_ops = {
13162a8a094STuomas Tynkkynen 	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
13262a8a094STuomas Tynkkynen 			   tegra_dfll_runtime_resume, NULL)
13362a8a094STuomas Tynkkynen };
13462a8a094STuomas Tynkkynen 
13562a8a094STuomas Tynkkynen static struct platform_driver tegra124_dfll_fcpu_driver = {
13662a8a094STuomas Tynkkynen 	.probe = tegra124_dfll_fcpu_probe,
13762a8a094STuomas Tynkkynen 	.remove = tegra_dfll_unregister,
13862a8a094STuomas Tynkkynen 	.driver = {
13962a8a094STuomas Tynkkynen 		.name = "tegra124-dfll",
14062a8a094STuomas Tynkkynen 		.of_match_table = tegra124_dfll_fcpu_of_match,
14162a8a094STuomas Tynkkynen 		.pm = &tegra124_dfll_pm_ops,
14262a8a094STuomas Tynkkynen 	},
14362a8a094STuomas Tynkkynen };
14462a8a094STuomas Tynkkynen 
14562a8a094STuomas Tynkkynen static int __init tegra124_dfll_fcpu_init(void)
14662a8a094STuomas Tynkkynen {
14762a8a094STuomas Tynkkynen 	return platform_driver_register(&tegra124_dfll_fcpu_driver);
14862a8a094STuomas Tynkkynen }
14962a8a094STuomas Tynkkynen module_init(tegra124_dfll_fcpu_init);
15062a8a094STuomas Tynkkynen 
15162a8a094STuomas Tynkkynen static void __exit tegra124_dfll_fcpu_exit(void)
15262a8a094STuomas Tynkkynen {
15362a8a094STuomas Tynkkynen 	platform_driver_unregister(&tegra124_dfll_fcpu_driver);
15462a8a094STuomas Tynkkynen }
15562a8a094STuomas Tynkkynen module_exit(tegra124_dfll_fcpu_exit);
15662a8a094STuomas Tynkkynen 
15762a8a094STuomas Tynkkynen MODULE_DESCRIPTION("Tegra124 DFLL clock source driver");
15862a8a094STuomas Tynkkynen MODULE_LICENSE("GPL v2");
15962a8a094STuomas Tynkkynen MODULE_AUTHOR("Aleksandr Frid <afrid@nvidia.com>");
16062a8a094STuomas Tynkkynen MODULE_AUTHOR("Paul Walmsley <pwalmsley@nvidia.com>");
161