diff options
author | Tuomas Tynkkynen <ttynkkynen@nvidia.com> | 2015-05-13 10:58:41 -0400 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2015-07-16 04:39:45 -0400 |
commit | 62a8a094b0e1de782a1b3dcb5e42a7d44379e583 (patch) | |
tree | 354de9066b07a1c4c3eb0fb7f727023b1a57b860 | |
parent | a3c83ff20c64a0ea3580aa7ed2953ff1602334dd (diff) |
clk: tegra: Add Tegra124 DFLL clocksource platform driver
Add basic platform driver support for the fast CPU cluster DFLL
clocksource found on Tegra124 SoCs. This small driver selects the
appropriate Tegra124-specific characterization data and integration
code. It relies on the DFLL common code to do most of the work.
Signed-off-by: Tuomas Tynkkynen <ttynkkynen@nvidia.com>
Signed-off-by: Mikko Perttunen <mikko.perttunen@kapsi.fi>
Acked-by: Michael Turquette <mturquette@linaro.org>
[treding@nvidia.com: move setup code into ->probe()]
Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r-- | drivers/clk/tegra/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-dfll.c | 6 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-dfll.h | 2 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 166 |
4 files changed, 172 insertions, 4 deletions
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index ec2e5163e1ae..826c325dc2e8 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile | |||
@@ -17,4 +17,6 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o | |||
17 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o | 17 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o |
18 | obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o | 18 | obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o |
19 | obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o | 19 | obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o |
20 | obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124-dfll-fcpu.o | ||
20 | obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o | 21 | obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o |
22 | obj-y += cvb.o | ||
diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c index 6ec645776897..109a79b95238 100644 --- a/drivers/clk/tegra/clk-dfll.c +++ b/drivers/clk/tegra/clk-dfll.c | |||
@@ -682,7 +682,7 @@ static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate) | |||
682 | struct dev_pm_opp *opp; | 682 | struct dev_pm_opp *opp; |
683 | int i, uv; | 683 | int i, uv; |
684 | 684 | ||
685 | opp = dev_pm_opp_find_freq_ceil(td->soc->opp_dev, &rate); | 685 | opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate); |
686 | if (IS_ERR(opp)) | 686 | if (IS_ERR(opp)) |
687 | return PTR_ERR(opp); | 687 | return PTR_ERR(opp); |
688 | uv = dev_pm_opp_get_voltage(opp); | 688 | uv = dev_pm_opp_get_voltage(opp); |
@@ -1436,7 +1436,7 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td) | |||
1436 | rcu_read_lock(); | 1436 | rcu_read_lock(); |
1437 | 1437 | ||
1438 | rate = ULONG_MAX; | 1438 | rate = ULONG_MAX; |
1439 | opp = dev_pm_opp_find_freq_floor(td->soc->opp_dev, &rate); | 1439 | opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate); |
1440 | if (IS_ERR(opp)) { | 1440 | if (IS_ERR(opp)) { |
1441 | dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n"); | 1441 | dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n"); |
1442 | goto out; | 1442 | goto out; |
@@ -1449,7 +1449,7 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td) | |||
1449 | goto out; | 1449 | goto out; |
1450 | 1450 | ||
1451 | for (j = 1, rate = 0; ; rate++) { | 1451 | for (j = 1, rate = 0; ; rate++) { |
1452 | opp = dev_pm_opp_find_freq_ceil(td->soc->opp_dev, &rate); | 1452 | opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate); |
1453 | if (IS_ERR(opp)) | 1453 | if (IS_ERR(opp)) |
1454 | break; | 1454 | break; |
1455 | v_opp = dev_pm_opp_get_voltage(opp); | 1455 | v_opp = dev_pm_opp_get_voltage(opp); |
diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h index b5d1fd47684e..2e4c0772a5dc 100644 --- a/drivers/clk/tegra/clk-dfll.h +++ b/drivers/clk/tegra/clk-dfll.h | |||
@@ -35,7 +35,7 @@ | |||
35 | * @set_clock_trimmers_low: fn ptr to tune clock trimmers for low voltage | 35 | * @set_clock_trimmers_low: fn ptr to tune clock trimmers for low voltage |
36 | */ | 36 | */ |
37 | struct tegra_dfll_soc_data { | 37 | struct tegra_dfll_soc_data { |
38 | struct device *opp_dev; | 38 | struct device *dev; |
39 | unsigned int min_millivolts; | 39 | unsigned int min_millivolts; |
40 | u32 tune0_low; | 40 | u32 tune0_low; |
41 | u32 tune0_high; | 41 | u32 tune0_high; |
diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c new file mode 100644 index 000000000000..61253330c12b --- /dev/null +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | |||
@@ -0,0 +1,166 @@ | |||
1 | /* | ||
2 | * Tegra124 DFLL FCPU clock source driver | ||
3 | * | ||
4 | * Copyright (C) 2012-2014 NVIDIA Corporation. All rights reserved. | ||
5 | * | ||
6 | * Aleksandr Frid <afrid@nvidia.com> | ||
7 | * Paul Walmsley <pwalmsley@nvidia.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
16 | * more details. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <linux/cpu.h> | ||
21 | #include <linux/err.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <soc/tegra/fuse.h> | ||
26 | |||
27 | #include "clk.h" | ||
28 | #include "clk-dfll.h" | ||
29 | #include "cvb.h" | ||
30 | |||
31 | /* Maximum CPU frequency, indexed by CPU speedo id */ | ||
32 | static const unsigned long cpu_max_freq_table[] = { | ||
33 | [0] = 2014500000UL, | ||
34 | [1] = 2320500000UL, | ||
35 | [2] = 2116500000UL, | ||
36 | [3] = 2524500000UL, | ||
37 | }; | ||
38 | |||
39 | static const struct cvb_table tegra124_cpu_cvb_tables[] = { | ||
40 | { | ||
41 | .speedo_id = -1, | ||
42 | .process_id = -1, | ||
43 | .min_millivolts = 900, | ||
44 | .max_millivolts = 1260, | ||
45 | .alignment = { | ||
46 | .step_uv = 10000, /* 10mV */ | ||
47 | }, | ||
48 | .speedo_scale = 100, | ||
49 | .voltage_scale = 1000, | ||
50 | .cvb_table = { | ||
51 | {204000000UL, {1112619, -29295, 402} }, | ||
52 | {306000000UL, {1150460, -30585, 402} }, | ||
53 | {408000000UL, {1190122, -31865, 402} }, | ||
54 | {510000000UL, {1231606, -33155, 402} }, | ||
55 | {612000000UL, {1274912, -34435, 402} }, | ||
56 | {714000000UL, {1320040, -35725, 402} }, | ||
57 | {816000000UL, {1366990, -37005, 402} }, | ||
58 | {918000000UL, {1415762, -38295, 402} }, | ||
59 | {1020000000UL, {1466355, -39575, 402} }, | ||
60 | {1122000000UL, {1518771, -40865, 402} }, | ||
61 | {1224000000UL, {1573009, -42145, 402} }, | ||
62 | {1326000000UL, {1629068, -43435, 402} }, | ||
63 | {1428000000UL, {1686950, -44715, 402} }, | ||
64 | {1530000000UL, {1746653, -46005, 402} }, | ||
65 | {1632000000UL, {1808179, -47285, 402} }, | ||
66 | {1734000000UL, {1871526, -48575, 402} }, | ||
67 | {1836000000UL, {1936696, -49855, 402} }, | ||
68 | {1938000000UL, {2003687, -51145, 402} }, | ||
69 | {2014500000UL, {2054787, -52095, 402} }, | ||
70 | {2116500000UL, {2124957, -53385, 402} }, | ||
71 | {2218500000UL, {2196950, -54665, 402} }, | ||
72 | {2320500000UL, {2270765, -55955, 402} }, | ||
73 | {2422500000UL, {2346401, -57235, 402} }, | ||
74 | {2524500000UL, {2437299, -58535, 402} }, | ||
75 | {0, { 0, 0, 0} }, | ||
76 | }, | ||
77 | .cpu_dfll_data = { | ||
78 | .tune0_low = 0x005020ff, | ||
79 | .tune0_high = 0x005040ff, | ||
80 | .tune1 = 0x00000060, | ||
81 | } | ||
82 | }, | ||
83 | }; | ||
84 | |||
85 | static int tegra124_dfll_fcpu_probe(struct platform_device *pdev) | ||
86 | { | ||
87 | int process_id, speedo_id, speedo_value; | ||
88 | struct tegra_dfll_soc_data *soc; | ||
89 | const struct cvb_table *cvb; | ||
90 | |||
91 | process_id = tegra_sku_info.cpu_process_id; | ||
92 | speedo_id = tegra_sku_info.cpu_speedo_id; | ||
93 | speedo_value = tegra_sku_info.cpu_speedo_value; | ||
94 | |||
95 | if (speedo_id >= ARRAY_SIZE(cpu_max_freq_table)) { | ||
96 | dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n", | ||
97 | speedo_id); | ||
98 | return -ENODEV; | ||
99 | } | ||
100 | |||
101 | soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL); | ||
102 | if (!soc) | ||
103 | return -ENOMEM; | ||
104 | |||
105 | soc->dev = get_cpu_device(0); | ||
106 | if (!soc->dev) { | ||
107 | dev_err(&pdev->dev, "no CPU0 device\n"); | ||
108 | return -ENODEV; | ||
109 | } | ||
110 | |||
111 | cvb = tegra_cvb_build_opp_table(tegra124_cpu_cvb_tables, | ||
112 | ARRAY_SIZE(tegra124_cpu_cvb_tables), | ||
113 | process_id, speedo_id, speedo_value, | ||
114 | cpu_max_freq_table[speedo_id], | ||
115 | soc->dev); | ||
116 | if (IS_ERR(cvb)) { | ||
117 | dev_err(&pdev->dev, "couldn't build OPP table: %ld\n", | ||
118 | PTR_ERR(cvb)); | ||
119 | return PTR_ERR(cvb); | ||
120 | } | ||
121 | |||
122 | soc->min_millivolts = cvb->min_millivolts; | ||
123 | soc->tune0_low = cvb->cpu_dfll_data.tune0_low; | ||
124 | soc->tune0_high = cvb->cpu_dfll_data.tune0_high; | ||
125 | soc->tune1 = cvb->cpu_dfll_data.tune1; | ||
126 | |||
127 | return tegra_dfll_register(pdev, soc); | ||
128 | } | ||
129 | |||
130 | static const struct of_device_id tegra124_dfll_fcpu_of_match[] = { | ||
131 | { .compatible = "nvidia,tegra124-dfll", }, | ||
132 | { }, | ||
133 | }; | ||
134 | MODULE_DEVICE_TABLE(of, tegra124_dfll_fcpu_of_match); | ||
135 | |||
136 | static const struct dev_pm_ops tegra124_dfll_pm_ops = { | ||
137 | SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend, | ||
138 | tegra_dfll_runtime_resume, NULL) | ||
139 | }; | ||
140 | |||
141 | static struct platform_driver tegra124_dfll_fcpu_driver = { | ||
142 | .probe = tegra124_dfll_fcpu_probe, | ||
143 | .remove = tegra_dfll_unregister, | ||
144 | .driver = { | ||
145 | .name = "tegra124-dfll", | ||
146 | .of_match_table = tegra124_dfll_fcpu_of_match, | ||
147 | .pm = &tegra124_dfll_pm_ops, | ||
148 | }, | ||
149 | }; | ||
150 | |||
151 | static int __init tegra124_dfll_fcpu_init(void) | ||
152 | { | ||
153 | return platform_driver_register(&tegra124_dfll_fcpu_driver); | ||
154 | } | ||
155 | module_init(tegra124_dfll_fcpu_init); | ||
156 | |||
157 | static void __exit tegra124_dfll_fcpu_exit(void) | ||
158 | { | ||
159 | platform_driver_unregister(&tegra124_dfll_fcpu_driver); | ||
160 | } | ||
161 | module_exit(tegra124_dfll_fcpu_exit); | ||
162 | |||
163 | MODULE_DESCRIPTION("Tegra124 DFLL clock source driver"); | ||
164 | MODULE_LICENSE("GPL v2"); | ||
165 | MODULE_AUTHOR("Aleksandr Frid <afrid@nvidia.com>"); | ||
166 | MODULE_AUTHOR("Paul Walmsley <pwalmsley@nvidia.com>"); | ||