xref: /linux/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c (revision b3cf8d0695056a370276c416979277635c3e4299)
162a8a094STuomas Tynkkynen /*
262a8a094STuomas Tynkkynen  * Tegra124 DFLL FCPU clock source driver
362a8a094STuomas Tynkkynen  *
4b0dcfb78SPeter De Schrijver  * Copyright (C) 2012-2019 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>
24b0dcfb78SPeter De Schrijver #include <linux/of_device.h>
2562a8a094STuomas Tynkkynen #include <linux/platform_device.h>
26*b3cf8d06SJoseph Lo #include <linux/regulator/consumer.h>
2762a8a094STuomas Tynkkynen #include <soc/tegra/fuse.h>
2862a8a094STuomas Tynkkynen 
2962a8a094STuomas Tynkkynen #include "clk.h"
3062a8a094STuomas Tynkkynen #include "clk-dfll.h"
3162a8a094STuomas Tynkkynen #include "cvb.h"
3262a8a094STuomas Tynkkynen 
33b0dcfb78SPeter De Schrijver struct dfll_fcpu_data {
34b0dcfb78SPeter De Schrijver 	const unsigned long *cpu_max_freq_table;
35b0dcfb78SPeter De Schrijver 	unsigned int cpu_max_freq_table_size;
36b0dcfb78SPeter De Schrijver 	const struct cvb_table *cpu_cvb_tables;
37b0dcfb78SPeter De Schrijver 	unsigned int cpu_cvb_tables_size;
38b0dcfb78SPeter De Schrijver };
39b0dcfb78SPeter De Schrijver 
4062a8a094STuomas Tynkkynen /* Maximum CPU frequency, indexed by CPU speedo id */
41b0dcfb78SPeter De Schrijver static const unsigned long tegra124_cpu_max_freq_table[] = {
4262a8a094STuomas Tynkkynen 	[0] = 2014500000UL,
4362a8a094STuomas Tynkkynen 	[1] = 2320500000UL,
4462a8a094STuomas Tynkkynen 	[2] = 2116500000UL,
4562a8a094STuomas Tynkkynen 	[3] = 2524500000UL,
4662a8a094STuomas Tynkkynen };
4762a8a094STuomas Tynkkynen 
4862a8a094STuomas Tynkkynen static const struct cvb_table tegra124_cpu_cvb_tables[] = {
4962a8a094STuomas Tynkkynen 	{
5062a8a094STuomas Tynkkynen 		.speedo_id = -1,
5162a8a094STuomas Tynkkynen 		.process_id = -1,
5262a8a094STuomas Tynkkynen 		.min_millivolts = 900,
5362a8a094STuomas Tynkkynen 		.max_millivolts = 1260,
5462a8a094STuomas Tynkkynen 		.speedo_scale = 100,
5562a8a094STuomas Tynkkynen 		.voltage_scale = 1000,
56e8f6a68cSThierry Reding 		.entries = {
5762a8a094STuomas Tynkkynen 			{  204000000UL, { 1112619, -29295, 402 } },
5862a8a094STuomas Tynkkynen 			{  306000000UL, { 1150460, -30585, 402 } },
5962a8a094STuomas Tynkkynen 			{  408000000UL, { 1190122, -31865, 402 } },
6062a8a094STuomas Tynkkynen 			{  510000000UL, { 1231606, -33155, 402 } },
6162a8a094STuomas Tynkkynen 			{  612000000UL, { 1274912, -34435, 402 } },
6262a8a094STuomas Tynkkynen 			{  714000000UL, { 1320040, -35725, 402 } },
6362a8a094STuomas Tynkkynen 			{  816000000UL, { 1366990, -37005, 402 } },
6462a8a094STuomas Tynkkynen 			{  918000000UL, { 1415762, -38295, 402 } },
6562a8a094STuomas Tynkkynen 			{ 1020000000UL, { 1466355, -39575, 402 } },
6662a8a094STuomas Tynkkynen 			{ 1122000000UL, { 1518771, -40865, 402 } },
6762a8a094STuomas Tynkkynen 			{ 1224000000UL, { 1573009, -42145, 402 } },
6862a8a094STuomas Tynkkynen 			{ 1326000000UL, { 1629068, -43435, 402 } },
6962a8a094STuomas Tynkkynen 			{ 1428000000UL, { 1686950, -44715, 402 } },
7062a8a094STuomas Tynkkynen 			{ 1530000000UL, { 1746653, -46005, 402 } },
7162a8a094STuomas Tynkkynen 			{ 1632000000UL, { 1808179, -47285, 402 } },
7262a8a094STuomas Tynkkynen 			{ 1734000000UL, { 1871526, -48575, 402 } },
7362a8a094STuomas Tynkkynen 			{ 1836000000UL, { 1936696, -49855, 402 } },
7462a8a094STuomas Tynkkynen 			{ 1938000000UL, { 2003687, -51145, 402 } },
7562a8a094STuomas Tynkkynen 			{ 2014500000UL, { 2054787, -52095, 402 } },
7662a8a094STuomas Tynkkynen 			{ 2116500000UL, { 2124957, -53385, 402 } },
7762a8a094STuomas Tynkkynen 			{ 2218500000UL, { 2196950, -54665, 402 } },
7862a8a094STuomas Tynkkynen 			{ 2320500000UL, { 2270765, -55955, 402 } },
7962a8a094STuomas Tynkkynen 			{ 2422500000UL, { 2346401, -57235, 402 } },
8062a8a094STuomas Tynkkynen 			{ 2524500000UL, { 2437299, -58535, 402 } },
812690e912SThierry Reding 			{          0UL, {       0,      0,   0 } },
8262a8a094STuomas Tynkkynen 		},
8362a8a094STuomas Tynkkynen 		.cpu_dfll_data = {
8462a8a094STuomas Tynkkynen 			.tune0_low = 0x005020ff,
8562a8a094STuomas Tynkkynen 			.tune0_high = 0x005040ff,
8662a8a094STuomas Tynkkynen 			.tune1 = 0x00000060,
8762a8a094STuomas Tynkkynen 		}
8862a8a094STuomas Tynkkynen 	},
8962a8a094STuomas Tynkkynen };
9062a8a094STuomas Tynkkynen 
91b0dcfb78SPeter De Schrijver static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
92b0dcfb78SPeter De Schrijver 	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
93b0dcfb78SPeter De Schrijver 	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
94b0dcfb78SPeter De Schrijver 	.cpu_cvb_tables = tegra124_cpu_cvb_tables,
95b0dcfb78SPeter De Schrijver 	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
96b0dcfb78SPeter De Schrijver };
97b0dcfb78SPeter De Schrijver 
98b0dcfb78SPeter De Schrijver static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
99b0dcfb78SPeter De Schrijver 	{
100b0dcfb78SPeter De Schrijver 		.compatible = "nvidia,tegra124-dfll",
101b0dcfb78SPeter De Schrijver 		.data = &tegra124_dfll_fcpu_data,
102b0dcfb78SPeter De Schrijver 	},
103b0dcfb78SPeter De Schrijver 	{ },
104b0dcfb78SPeter De Schrijver };
105b0dcfb78SPeter De Schrijver 
106*b3cf8d06SJoseph Lo static void get_alignment_from_dt(struct device *dev,
107*b3cf8d06SJoseph Lo 				  struct rail_alignment *align)
108*b3cf8d06SJoseph Lo {
109*b3cf8d06SJoseph Lo 	if (of_property_read_u32(dev->of_node,
110*b3cf8d06SJoseph Lo 				 "nvidia,pwm-voltage-step-microvolts",
111*b3cf8d06SJoseph Lo 				 &align->step_uv))
112*b3cf8d06SJoseph Lo 		align->step_uv = 0;
113*b3cf8d06SJoseph Lo 
114*b3cf8d06SJoseph Lo 	if (of_property_read_u32(dev->of_node,
115*b3cf8d06SJoseph Lo 				 "nvidia,pwm-min-microvolts",
116*b3cf8d06SJoseph Lo 				 &align->offset_uv))
117*b3cf8d06SJoseph Lo 		align->offset_uv = 0;
118*b3cf8d06SJoseph Lo }
119*b3cf8d06SJoseph Lo 
120*b3cf8d06SJoseph Lo static int get_alignment_from_regulator(struct device *dev,
121*b3cf8d06SJoseph Lo 					 struct rail_alignment *align)
122*b3cf8d06SJoseph Lo {
123*b3cf8d06SJoseph Lo 	struct regulator *reg = devm_regulator_get(dev, "vdd-cpu");
124*b3cf8d06SJoseph Lo 
125*b3cf8d06SJoseph Lo 	if (IS_ERR(reg))
126*b3cf8d06SJoseph Lo 		return PTR_ERR(reg);
127*b3cf8d06SJoseph Lo 
128*b3cf8d06SJoseph Lo 	align->offset_uv = regulator_list_voltage(reg, 0);
129*b3cf8d06SJoseph Lo 	align->step_uv = regulator_get_linear_step(reg);
130*b3cf8d06SJoseph Lo 
131*b3cf8d06SJoseph Lo 	devm_regulator_put(reg);
132*b3cf8d06SJoseph Lo 
133*b3cf8d06SJoseph Lo 	return 0;
134*b3cf8d06SJoseph Lo }
135*b3cf8d06SJoseph Lo 
13662a8a094STuomas Tynkkynen static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
13762a8a094STuomas Tynkkynen {
138f7c42d98SThierry Reding 	int process_id, speedo_id, speedo_value, err;
13962a8a094STuomas Tynkkynen 	struct tegra_dfll_soc_data *soc;
140b0dcfb78SPeter De Schrijver 	const struct dfll_fcpu_data *fcpu_data;
141*b3cf8d06SJoseph Lo 	struct rail_alignment align;
142b0dcfb78SPeter De Schrijver 
143b0dcfb78SPeter De Schrijver 	fcpu_data = of_device_get_match_data(&pdev->dev);
144b0dcfb78SPeter De Schrijver 	if (!fcpu_data)
145b0dcfb78SPeter De Schrijver 		return -ENODEV;
14662a8a094STuomas Tynkkynen 
14762a8a094STuomas Tynkkynen 	process_id = tegra_sku_info.cpu_process_id;
14862a8a094STuomas Tynkkynen 	speedo_id = tegra_sku_info.cpu_speedo_id;
14962a8a094STuomas Tynkkynen 	speedo_value = tegra_sku_info.cpu_speedo_value;
15062a8a094STuomas Tynkkynen 
151b0dcfb78SPeter De Schrijver 	if (speedo_id >= fcpu_data->cpu_max_freq_table_size) {
15262a8a094STuomas Tynkkynen 		dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n",
15362a8a094STuomas Tynkkynen 			speedo_id);
15462a8a094STuomas Tynkkynen 		return -ENODEV;
15562a8a094STuomas Tynkkynen 	}
15662a8a094STuomas Tynkkynen 
15762a8a094STuomas Tynkkynen 	soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL);
15862a8a094STuomas Tynkkynen 	if (!soc)
15962a8a094STuomas Tynkkynen 		return -ENOMEM;
16062a8a094STuomas Tynkkynen 
16162a8a094STuomas Tynkkynen 	soc->dev = get_cpu_device(0);
16262a8a094STuomas Tynkkynen 	if (!soc->dev) {
16362a8a094STuomas Tynkkynen 		dev_err(&pdev->dev, "no CPU0 device\n");
16462a8a094STuomas Tynkkynen 		return -ENODEV;
16562a8a094STuomas Tynkkynen 	}
16662a8a094STuomas Tynkkynen 
167*b3cf8d06SJoseph Lo 	if (of_property_read_bool(pdev->dev.of_node, "nvidia,pwm-to-pmic")) {
168*b3cf8d06SJoseph Lo 		get_alignment_from_dt(&pdev->dev, &align);
169*b3cf8d06SJoseph Lo 	} else {
170*b3cf8d06SJoseph Lo 		err = get_alignment_from_regulator(&pdev->dev, &align);
171*b3cf8d06SJoseph Lo 		if (err)
172*b3cf8d06SJoseph Lo 			return err;
173*b3cf8d06SJoseph Lo 	}
174*b3cf8d06SJoseph Lo 
175b0dcfb78SPeter De Schrijver 	soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id];
176f7c42d98SThierry Reding 
177b0dcfb78SPeter De Schrijver 	soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
178b0dcfb78SPeter De Schrijver 					   fcpu_data->cpu_cvb_tables_size,
179*b3cf8d06SJoseph Lo 					   &align, process_id, speedo_id,
180*b3cf8d06SJoseph Lo 					   speedo_value, soc->max_freq);
181*b3cf8d06SJoseph Lo 	soc->alignment = align;
182*b3cf8d06SJoseph Lo 
18327ed2f7eSThierry Reding 	if (IS_ERR(soc->cvb)) {
18427ed2f7eSThierry Reding 		dev_err(&pdev->dev, "couldn't add OPP table: %ld\n",
18527ed2f7eSThierry Reding 			PTR_ERR(soc->cvb));
18627ed2f7eSThierry Reding 		return PTR_ERR(soc->cvb);
18762a8a094STuomas Tynkkynen 	}
18862a8a094STuomas Tynkkynen 
189f7c42d98SThierry Reding 	err = tegra_dfll_register(pdev, soc);
190f7c42d98SThierry Reding 	if (err < 0) {
191f7c42d98SThierry Reding 		tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq);
192f7c42d98SThierry Reding 		return err;
193f7c42d98SThierry Reding 	}
19462a8a094STuomas Tynkkynen 
195f7c42d98SThierry Reding 	return 0;
196f7c42d98SThierry Reding }
197f7c42d98SThierry Reding 
198f7c42d98SThierry Reding static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
199f7c42d98SThierry Reding {
2001752c9eeSNicolin Chen 	struct tegra_dfll_soc_data *soc;
201f7c42d98SThierry Reding 
2021752c9eeSNicolin Chen 	soc = tegra_dfll_unregister(pdev);
2031752c9eeSNicolin Chen 	if (IS_ERR(soc))
2041752c9eeSNicolin Chen 		dev_err(&pdev->dev, "failed to unregister DFLL: %ld\n",
2051752c9eeSNicolin Chen 			PTR_ERR(soc));
206f7c42d98SThierry Reding 
207f7c42d98SThierry Reding 	tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq);
208f7c42d98SThierry Reding 
209f7c42d98SThierry Reding 	return 0;
21062a8a094STuomas Tynkkynen }
21162a8a094STuomas Tynkkynen 
21262a8a094STuomas Tynkkynen static const struct dev_pm_ops tegra124_dfll_pm_ops = {
21362a8a094STuomas Tynkkynen 	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
21462a8a094STuomas Tynkkynen 			   tegra_dfll_runtime_resume, NULL)
21562a8a094STuomas Tynkkynen };
21662a8a094STuomas Tynkkynen 
21762a8a094STuomas Tynkkynen static struct platform_driver tegra124_dfll_fcpu_driver = {
21862a8a094STuomas Tynkkynen 	.probe = tegra124_dfll_fcpu_probe,
219f7c42d98SThierry Reding 	.remove = tegra124_dfll_fcpu_remove,
22062a8a094STuomas Tynkkynen 	.driver = {
22162a8a094STuomas Tynkkynen 		.name = "tegra124-dfll",
22262a8a094STuomas Tynkkynen 		.of_match_table = tegra124_dfll_fcpu_of_match,
22362a8a094STuomas Tynkkynen 		.pm = &tegra124_dfll_pm_ops,
22462a8a094STuomas Tynkkynen 	},
22562a8a094STuomas Tynkkynen };
2266f877e79SWei Yongjun builtin_platform_driver(tegra124_dfll_fcpu_driver);
227