diff options
author | Wei Ni <wni@nvidia.com> | 2016-03-29 06:29:14 -0400 |
---|---|---|
committer | Eduardo Valentin <edubezval@gmail.com> | 2016-05-17 10:28:28 -0400 |
commit | 65b6d57c24ed0aff1fc571e42d8f51bdfcce9a8e (patch) | |
tree | 3e2594f80a4d68d263cfe28b6ed68c53dd46b052 | |
parent | 1c3bdc1627c331b4925a95453f7b61226077bebe (diff) |
thermal: tegra: split tegra_soctherm driver
Split most of the Tegra124 data and code into a Tegra124-specific
file.
Split most of the fuse-related code into a fuse-related source file.
This is in preparation for adding a Tegra210-specific driver in a
future patch.
Beyond the maintainability improvements, this is intended to separate
chip-specific ATE and characterization-related hacks into chip-specific
files, in the hopes that they won't pollute code for other chips.
Signed-off-by: Wei Ni <wni@nvidia.com>
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
-rw-r--r-- | drivers/thermal/tegra/Kconfig | 2 | ||||
-rw-r--r-- | drivers/thermal/tegra/Makefile | 3 | ||||
-rw-r--r-- | drivers/thermal/tegra/soctherm-fuse.c | 158 | ||||
-rw-r--r-- | drivers/thermal/tegra/soctherm.c | 310 | ||||
-rw-r--r-- | drivers/thermal/tegra/soctherm.h | 110 | ||||
-rw-r--r-- | drivers/thermal/tegra/tegra-soctherm.c | 565 | ||||
-rw-r--r-- | drivers/thermal/tegra/tegra124-soctherm.c | 172 |
7 files changed, 754 insertions, 566 deletions
diff --git a/drivers/thermal/tegra/Kconfig b/drivers/thermal/tegra/Kconfig index bed898858d02..cec586ec7e4b 100644 --- a/drivers/thermal/tegra/Kconfig +++ b/drivers/thermal/tegra/Kconfig | |||
@@ -5,7 +5,7 @@ config TEGRA_SOCTHERM | |||
5 | tristate "Tegra SOCTHERM thermal management" | 5 | tristate "Tegra SOCTHERM thermal management" |
6 | help | 6 | help |
7 | Enable this option for integrated thermal management support on NVIDIA | 7 | Enable this option for integrated thermal management support on NVIDIA |
8 | Tegra124 systems-on-chip. The driver supports four thermal zones | 8 | Tegra systems-on-chip. The driver supports four thermal zones |
9 | (CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal | 9 | (CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal |
10 | zones to manage temperatures. This option is also required for the | 10 | zones to manage temperatures. This option is also required for the |
11 | emergency thermal reset (thermtrip) feature to function. | 11 | emergency thermal reset (thermtrip) feature to function. |
diff --git a/drivers/thermal/tegra/Makefile b/drivers/thermal/tegra/Makefile index d4dc4e7f279e..d5fb15377b97 100644 --- a/drivers/thermal/tegra/Makefile +++ b/drivers/thermal/tegra/Makefile | |||
@@ -1 +1,4 @@ | |||
1 | obj-$(CONFIG_TEGRA_SOCTHERM) += tegra-soctherm.o | 1 | obj-$(CONFIG_TEGRA_SOCTHERM) += tegra-soctherm.o |
2 | |||
3 | tegra-soctherm-y := soctherm.o soctherm-fuse.o | ||
4 | tegra-soctherm-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124-soctherm.o | ||
diff --git a/drivers/thermal/tegra/soctherm-fuse.c b/drivers/thermal/tegra/soctherm-fuse.c new file mode 100644 index 000000000000..931c299ab0e8 --- /dev/null +++ b/drivers/thermal/tegra/soctherm-fuse.c | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <soc/tegra/fuse.h> | ||
18 | |||
19 | #include "soctherm.h" | ||
20 | |||
21 | #define NOMINAL_CALIB_FT 105 | ||
22 | #define NOMINAL_CALIB_CP 25 | ||
23 | |||
24 | #define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff | ||
25 | #define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13) | ||
26 | #define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13 | ||
27 | |||
28 | #define FUSE_TSENSOR_COMMON 0x180 | ||
29 | |||
30 | /* | ||
31 | * Tegra12x, etc: | ||
32 | * FUSE_TSENSOR_COMMON: | ||
33 | * 3 2 1 0 | ||
34 | * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 | ||
35 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
36 | * |-----------| SHFT_FT | BASE_FT | BASE_CP | | ||
37 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
38 | * | ||
39 | * FUSE_SPARE_REALIGNMENT_REG: | ||
40 | * 3 2 1 0 | ||
41 | * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 | ||
42 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
43 | * |---------------------------------------------------| SHIFT_CP | | ||
44 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
45 | */ | ||
46 | |||
47 | #define CALIB_COEFFICIENT 1000000LL | ||
48 | |||
49 | /** | ||
50 | * div64_s64_precise() - wrapper for div64_s64() | ||
51 | * @a: the dividend | ||
52 | * @b: the divisor | ||
53 | * | ||
54 | * Implements division with fairly accurate rounding instead of truncation by | ||
55 | * shifting the dividend to the left by 16 so that the quotient has a | ||
56 | * much higher precision. | ||
57 | * | ||
58 | * Return: the quotient of a / b. | ||
59 | */ | ||
60 | static s64 div64_s64_precise(s64 a, s32 b) | ||
61 | { | ||
62 | s64 r, al; | ||
63 | |||
64 | /* Scale up for increased precision division */ | ||
65 | al = a << 16; | ||
66 | |||
67 | r = div64_s64(al * 2 + 1, 2 * b); | ||
68 | return r >> 16; | ||
69 | } | ||
70 | |||
71 | int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse, | ||
72 | struct tsensor_shared_calib *shared) | ||
73 | { | ||
74 | u32 val; | ||
75 | s32 shifted_cp, shifted_ft; | ||
76 | int err; | ||
77 | |||
78 | err = tegra_fuse_readl(FUSE_TSENSOR_COMMON, &val); | ||
79 | if (err) | ||
80 | return err; | ||
81 | |||
82 | shared->base_cp = (val & tfuse->fuse_base_cp_mask) >> | ||
83 | tfuse->fuse_base_cp_shift; | ||
84 | shared->base_ft = (val & tfuse->fuse_base_ft_mask) >> | ||
85 | tfuse->fuse_base_ft_shift; | ||
86 | |||
87 | shifted_ft = (val & tfuse->fuse_shift_ft_mask) >> | ||
88 | tfuse->fuse_shift_ft_shift; | ||
89 | shifted_ft = sign_extend32(shifted_ft, 4); | ||
90 | |||
91 | if (tfuse->fuse_spare_realignment) { | ||
92 | err = tegra_fuse_readl(tfuse->fuse_spare_realignment, &val); | ||
93 | if (err) | ||
94 | return err; | ||
95 | } | ||
96 | |||
97 | shifted_cp = sign_extend32(val, 5); | ||
98 | |||
99 | shared->actual_temp_cp = 2 * NOMINAL_CALIB_CP + shifted_cp; | ||
100 | shared->actual_temp_ft = 2 * NOMINAL_CALIB_FT + shifted_ft; | ||
101 | |||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor, | ||
106 | const struct tsensor_shared_calib *shared, | ||
107 | u32 *calibration) | ||
108 | { | ||
109 | const struct tegra_tsensor_group *sensor_group; | ||
110 | u32 val, calib; | ||
111 | s32 actual_tsensor_ft, actual_tsensor_cp; | ||
112 | s32 delta_sens, delta_temp; | ||
113 | s32 mult, div; | ||
114 | s16 therma, thermb; | ||
115 | s64 temp; | ||
116 | int err; | ||
117 | |||
118 | sensor_group = sensor->group; | ||
119 | |||
120 | err = tegra_fuse_readl(sensor->calib_fuse_offset, &val); | ||
121 | if (err) | ||
122 | return err; | ||
123 | |||
124 | actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12); | ||
125 | val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK) >> | ||
126 | FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT; | ||
127 | actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12); | ||
128 | |||
129 | delta_sens = actual_tsensor_ft - actual_tsensor_cp; | ||
130 | delta_temp = shared->actual_temp_ft - shared->actual_temp_cp; | ||
131 | |||
132 | mult = sensor_group->pdiv * sensor->config->tsample_ate; | ||
133 | div = sensor->config->tsample * sensor_group->pdiv_ate; | ||
134 | |||
135 | temp = (s64)delta_temp * (1LL << 13) * mult; | ||
136 | therma = div64_s64_precise(temp, (s64)delta_sens * div); | ||
137 | |||
138 | temp = ((s64)actual_tsensor_ft * shared->actual_temp_cp) - | ||
139 | ((s64)actual_tsensor_cp * shared->actual_temp_ft); | ||
140 | thermb = div64_s64_precise(temp, delta_sens); | ||
141 | |||
142 | temp = (s64)therma * sensor->fuse_corr_alpha; | ||
143 | therma = div64_s64_precise(temp, CALIB_COEFFICIENT); | ||
144 | |||
145 | temp = (s64)thermb * sensor->fuse_corr_alpha + sensor->fuse_corr_beta; | ||
146 | thermb = div64_s64_precise(temp, CALIB_COEFFICIENT); | ||
147 | |||
148 | calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) | | ||
149 | ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT); | ||
150 | |||
151 | *calibration = calib; | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | MODULE_AUTHOR("Wei Ni <wni@nvidia.com>"); | ||
157 | MODULE_DESCRIPTION("Tegra SOCTHERM fuse management"); | ||
158 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c new file mode 100644 index 000000000000..d9b23cded69f --- /dev/null +++ b/drivers/thermal/tegra/soctherm.c | |||
@@ -0,0 +1,310 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * Author: | ||
5 | * Mikko Perttunen <mperttunen@nvidia.com> | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/bitops.h> | ||
19 | #include <linux/clk.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/err.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/of.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/reset.h> | ||
28 | #include <linux/thermal.h> | ||
29 | |||
30 | #include <dt-bindings/thermal/tegra124-soctherm.h> | ||
31 | |||
32 | #include "soctherm.h" | ||
33 | |||
34 | #define SENSOR_CONFIG0 0 | ||
35 | #define SENSOR_CONFIG0_STOP BIT(0) | ||
36 | #define SENSOR_CONFIG0_TALL_SHIFT 8 | ||
37 | #define SENSOR_CONFIG0_TCALC_OVER BIT(4) | ||
38 | #define SENSOR_CONFIG0_OVER BIT(3) | ||
39 | #define SENSOR_CONFIG0_CPTR_OVER BIT(2) | ||
40 | |||
41 | #define SENSOR_CONFIG1 4 | ||
42 | #define SENSOR_CONFIG1_TSAMPLE_SHIFT 0 | ||
43 | #define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15 | ||
44 | #define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24 | ||
45 | #define SENSOR_CONFIG1_TEMP_ENABLE BIT(31) | ||
46 | |||
47 | /* | ||
48 | * SENSOR_CONFIG2 is defined in soctherm.h | ||
49 | * because, it will be used by tegra_soctherm_fuse.c | ||
50 | */ | ||
51 | |||
52 | #define READBACK_VALUE_MASK 0xff00 | ||
53 | #define READBACK_VALUE_SHIFT 8 | ||
54 | #define READBACK_ADD_HALF BIT(7) | ||
55 | #define READBACK_NEGATE BIT(0) | ||
56 | |||
57 | /* get val from register(r) mask bits(m) */ | ||
58 | #define REG_GET_MASK(r, m) (((r) & (m)) >> (ffs(m) - 1)) | ||
59 | /* set val(v) to mask bits(m) of register(r) */ | ||
60 | #define REG_SET_MASK(r, m, v) (((r) & ~(m)) | \ | ||
61 | (((v) & (m >> (ffs(m) - 1))) << (ffs(m) - 1))) | ||
62 | |||
63 | struct tegra_thermctl_zone { | ||
64 | void __iomem *reg; | ||
65 | u32 mask; | ||
66 | }; | ||
67 | |||
68 | struct tegra_soctherm { | ||
69 | struct reset_control *reset; | ||
70 | struct clk *clock_tsensor; | ||
71 | struct clk *clock_soctherm; | ||
72 | void __iomem *regs; | ||
73 | |||
74 | u32 *calib; | ||
75 | struct tegra_soctherm_soc *soc; | ||
76 | }; | ||
77 | |||
78 | static int enable_tsensor(struct tegra_soctherm *tegra, | ||
79 | unsigned int i, | ||
80 | const struct tsensor_shared_calib *shared) | ||
81 | { | ||
82 | const struct tegra_tsensor *sensor = &tegra->soc->tsensors[i]; | ||
83 | void __iomem *base = tegra->regs + sensor->base; | ||
84 | u32 *calib = &tegra->calib[i]; | ||
85 | unsigned int val; | ||
86 | int err; | ||
87 | |||
88 | err = tegra_calc_tsensor_calib(sensor, shared, calib); | ||
89 | if (err) | ||
90 | return err; | ||
91 | |||
92 | val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT; | ||
93 | writel(val, base + SENSOR_CONFIG0); | ||
94 | |||
95 | val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT; | ||
96 | val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT; | ||
97 | val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT; | ||
98 | val |= SENSOR_CONFIG1_TEMP_ENABLE; | ||
99 | writel(val, base + SENSOR_CONFIG1); | ||
100 | |||
101 | writel(*calib, base + SENSOR_CONFIG2); | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * Translate from soctherm readback format to millicelsius. | ||
108 | * The soctherm readback format in bits is as follows: | ||
109 | * TTTTTTTT H______N | ||
110 | * where T's contain the temperature in Celsius, | ||
111 | * H denotes an addition of 0.5 Celsius and N denotes negation | ||
112 | * of the final value. | ||
113 | */ | ||
114 | static int translate_temp(u16 val) | ||
115 | { | ||
116 | int t; | ||
117 | |||
118 | t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000; | ||
119 | if (val & READBACK_ADD_HALF) | ||
120 | t += 500; | ||
121 | if (val & READBACK_NEGATE) | ||
122 | t *= -1; | ||
123 | |||
124 | return t; | ||
125 | } | ||
126 | |||
127 | static int tegra_thermctl_get_temp(void *data, int *out_temp) | ||
128 | { | ||
129 | struct tegra_thermctl_zone *zone = data; | ||
130 | u32 val; | ||
131 | |||
132 | val = readl(zone->reg); | ||
133 | val = REG_GET_MASK(val, zone->mask); | ||
134 | *out_temp = translate_temp(val); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = { | ||
140 | .get_temp = tegra_thermctl_get_temp, | ||
141 | }; | ||
142 | |||
143 | static const struct of_device_id tegra_soctherm_of_match[] = { | ||
144 | #ifdef CONFIG_ARCH_TEGRA_124_SOC | ||
145 | { | ||
146 | .compatible = "nvidia,tegra124-soctherm", | ||
147 | .data = &tegra124_soctherm, | ||
148 | }, | ||
149 | #endif | ||
150 | { }, | ||
151 | }; | ||
152 | MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match); | ||
153 | |||
154 | static int tegra_soctherm_probe(struct platform_device *pdev) | ||
155 | { | ||
156 | const struct of_device_id *match; | ||
157 | struct tegra_soctherm *tegra; | ||
158 | struct thermal_zone_device *z; | ||
159 | struct tsensor_shared_calib shared_calib; | ||
160 | struct resource *res; | ||
161 | struct tegra_soctherm_soc *soc; | ||
162 | unsigned int i; | ||
163 | int err; | ||
164 | u32 pdiv, hotspot; | ||
165 | |||
166 | match = of_match_node(tegra_soctherm_of_match, pdev->dev.of_node); | ||
167 | if (!match) | ||
168 | return -ENODEV; | ||
169 | |||
170 | soc = (struct tegra_soctherm_soc *)match->data; | ||
171 | if (soc->num_ttgs > TEGRA124_SOCTHERM_SENSOR_NUM) | ||
172 | return -EINVAL; | ||
173 | |||
174 | tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); | ||
175 | if (!tegra) | ||
176 | return -ENOMEM; | ||
177 | |||
178 | dev_set_drvdata(&pdev->dev, tegra); | ||
179 | |||
180 | tegra->soc = soc; | ||
181 | |||
182 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
183 | tegra->regs = devm_ioremap_resource(&pdev->dev, res); | ||
184 | if (IS_ERR(tegra->regs)) | ||
185 | return PTR_ERR(tegra->regs); | ||
186 | |||
187 | tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm"); | ||
188 | if (IS_ERR(tegra->reset)) { | ||
189 | dev_err(&pdev->dev, "can't get soctherm reset\n"); | ||
190 | return PTR_ERR(tegra->reset); | ||
191 | } | ||
192 | |||
193 | tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor"); | ||
194 | if (IS_ERR(tegra->clock_tsensor)) { | ||
195 | dev_err(&pdev->dev, "can't get tsensor clock\n"); | ||
196 | return PTR_ERR(tegra->clock_tsensor); | ||
197 | } | ||
198 | |||
199 | tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm"); | ||
200 | if (IS_ERR(tegra->clock_soctherm)) { | ||
201 | dev_err(&pdev->dev, "can't get soctherm clock\n"); | ||
202 | return PTR_ERR(tegra->clock_soctherm); | ||
203 | } | ||
204 | |||
205 | reset_control_assert(tegra->reset); | ||
206 | |||
207 | err = clk_prepare_enable(tegra->clock_soctherm); | ||
208 | if (err) | ||
209 | return err; | ||
210 | |||
211 | err = clk_prepare_enable(tegra->clock_tsensor); | ||
212 | if (err) { | ||
213 | clk_disable_unprepare(tegra->clock_soctherm); | ||
214 | return err; | ||
215 | } | ||
216 | |||
217 | reset_control_deassert(tegra->reset); | ||
218 | |||
219 | /* Initialize raw sensors */ | ||
220 | |||
221 | tegra->calib = devm_kzalloc(&pdev->dev, | ||
222 | sizeof(u32) * soc->num_tsensors, | ||
223 | GFP_KERNEL); | ||
224 | if (!tegra->calib) { | ||
225 | err = -ENOMEM; | ||
226 | goto disable_clocks; | ||
227 | } | ||
228 | |||
229 | err = tegra_calc_shared_calib(soc->tfuse, &shared_calib); | ||
230 | if (err) | ||
231 | goto disable_clocks; | ||
232 | |||
233 | for (i = 0; i < soc->num_tsensors; ++i) { | ||
234 | err = enable_tsensor(tegra, i, &shared_calib); | ||
235 | if (err) | ||
236 | goto disable_clocks; | ||
237 | } | ||
238 | |||
239 | /* Program pdiv and hotspot offsets per THERM */ | ||
240 | pdiv = readl(tegra->regs + SENSOR_PDIV); | ||
241 | hotspot = readl(tegra->regs + SENSOR_HOTSPOT_OFF); | ||
242 | for (i = 0; i < soc->num_ttgs; ++i) { | ||
243 | pdiv = REG_SET_MASK(pdiv, soc->ttgs[i]->pdiv_mask, | ||
244 | soc->ttgs[i]->pdiv); | ||
245 | /* hotspot offset from PLLX, doesn't need to configure PLLX */ | ||
246 | if (soc->ttgs[i]->id == TEGRA124_SOCTHERM_SENSOR_PLLX) | ||
247 | continue; | ||
248 | hotspot = REG_SET_MASK(hotspot, | ||
249 | soc->ttgs[i]->pllx_hotspot_mask, | ||
250 | soc->ttgs[i]->pllx_hotspot_diff); | ||
251 | } | ||
252 | writel(pdiv, tegra->regs + SENSOR_PDIV); | ||
253 | writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF); | ||
254 | |||
255 | /* Initialize thermctl sensors */ | ||
256 | |||
257 | for (i = 0; i < soc->num_ttgs; ++i) { | ||
258 | struct tegra_thermctl_zone *zone = | ||
259 | devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL); | ||
260 | if (!zone) { | ||
261 | err = -ENOMEM; | ||
262 | goto disable_clocks; | ||
263 | } | ||
264 | |||
265 | zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset; | ||
266 | zone->mask = soc->ttgs[i]->sensor_temp_mask; | ||
267 | |||
268 | z = devm_thermal_zone_of_sensor_register(&pdev->dev, | ||
269 | soc->ttgs[i]->id, zone, | ||
270 | &tegra_of_thermal_ops); | ||
271 | if (IS_ERR(z)) { | ||
272 | err = PTR_ERR(z); | ||
273 | dev_err(&pdev->dev, "failed to register sensor: %d\n", | ||
274 | err); | ||
275 | goto disable_clocks; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | return 0; | ||
280 | |||
281 | disable_clocks: | ||
282 | clk_disable_unprepare(tegra->clock_tsensor); | ||
283 | clk_disable_unprepare(tegra->clock_soctherm); | ||
284 | |||
285 | return err; | ||
286 | } | ||
287 | |||
288 | static int tegra_soctherm_remove(struct platform_device *pdev) | ||
289 | { | ||
290 | struct tegra_soctherm *tegra = platform_get_drvdata(pdev); | ||
291 | |||
292 | clk_disable_unprepare(tegra->clock_tsensor); | ||
293 | clk_disable_unprepare(tegra->clock_soctherm); | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static struct platform_driver tegra_soctherm_driver = { | ||
299 | .probe = tegra_soctherm_probe, | ||
300 | .remove = tegra_soctherm_remove, | ||
301 | .driver = { | ||
302 | .name = "tegra_soctherm", | ||
303 | .of_match_table = tegra_soctherm_of_match, | ||
304 | }, | ||
305 | }; | ||
306 | module_platform_driver(tegra_soctherm_driver); | ||
307 | |||
308 | MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); | ||
309 | MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver"); | ||
310 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/thermal/tegra/soctherm.h b/drivers/thermal/tegra/soctherm.h new file mode 100644 index 000000000000..f80ee1492ddb --- /dev/null +++ b/drivers/thermal/tegra/soctherm.h | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef __DRIVERS_THERMAL_TEGRA_SOCTHERM_H | ||
16 | #define __DRIVERS_THERMAL_TEGRA_SOCTHERM_H | ||
17 | |||
18 | #define SENSOR_CONFIG2 8 | ||
19 | #define SENSOR_CONFIG2_THERMA_SHIFT 16 | ||
20 | #define SENSOR_CONFIG2_THERMB_SHIFT 0 | ||
21 | |||
22 | #define SENSOR_PDIV 0x1c0 | ||
23 | #define SENSOR_PDIV_CPU_MASK (0xf << 12) | ||
24 | #define SENSOR_PDIV_GPU_MASK (0xf << 8) | ||
25 | #define SENSOR_PDIV_MEM_MASK (0xf << 4) | ||
26 | #define SENSOR_PDIV_PLLX_MASK (0xf << 0) | ||
27 | |||
28 | #define SENSOR_HOTSPOT_OFF 0x1c4 | ||
29 | #define SENSOR_HOTSPOT_CPU_MASK (0xff << 16) | ||
30 | #define SENSOR_HOTSPOT_GPU_MASK (0xff << 8) | ||
31 | #define SENSOR_HOTSPOT_MEM_MASK (0xff << 0) | ||
32 | |||
33 | #define SENSOR_TEMP1 0x1c8 | ||
34 | #define SENSOR_TEMP1_CPU_TEMP_MASK (0xffff << 16) | ||
35 | #define SENSOR_TEMP1_GPU_TEMP_MASK 0xffff | ||
36 | #define SENSOR_TEMP2 0x1cc | ||
37 | #define SENSOR_TEMP2_MEM_TEMP_MASK (0xffff << 16) | ||
38 | #define SENSOR_TEMP2_PLLX_TEMP_MASK 0xffff | ||
39 | |||
40 | /** | ||
41 | * struct tegra_tsensor_group - SOC_THERM sensor group data | ||
42 | * @name: short name of the temperature sensor group | ||
43 | * @id: numeric ID of the temperature sensor group | ||
44 | * @sensor_temp_offset: offset of the SENSOR_TEMP* register | ||
45 | * @sensor_temp_mask: bit mask for this sensor group in SENSOR_TEMP* register | ||
46 | * @pdiv: the sensor count post-divider to use during runtime | ||
47 | * @pdiv_ate: the sensor count post-divider used during automated test | ||
48 | * @pdiv_mask: register bitfield mask for the PDIV field for this sensor | ||
49 | * @pllx_hotspot_diff: hotspot offset from the PLLX sensor, must be 0 for | ||
50 | PLLX sensor group | ||
51 | * @pllx_hotspot_mask: register bitfield mask for the HOTSPOT field | ||
52 | */ | ||
53 | struct tegra_tsensor_group { | ||
54 | const char *name; | ||
55 | u8 id; | ||
56 | u16 sensor_temp_offset; | ||
57 | u32 sensor_temp_mask; | ||
58 | u32 pdiv, pdiv_ate, pdiv_mask; | ||
59 | u32 pllx_hotspot_diff, pllx_hotspot_mask; | ||
60 | }; | ||
61 | |||
62 | struct tegra_tsensor_configuration { | ||
63 | u32 tall, tiddq_en, ten_count, pdiv, pdiv_ate, tsample, tsample_ate; | ||
64 | }; | ||
65 | |||
66 | struct tegra_tsensor { | ||
67 | const char *name; | ||
68 | const u32 base; | ||
69 | const struct tegra_tsensor_configuration *config; | ||
70 | const u32 calib_fuse_offset; | ||
71 | /* | ||
72 | * Correction values used to modify values read from | ||
73 | * calibration fuses | ||
74 | */ | ||
75 | const s32 fuse_corr_alpha, fuse_corr_beta; | ||
76 | const struct tegra_tsensor_group *group; | ||
77 | }; | ||
78 | |||
79 | struct tegra_soctherm_fuse { | ||
80 | u32 fuse_base_cp_mask, fuse_base_cp_shift; | ||
81 | u32 fuse_base_ft_mask, fuse_base_ft_shift; | ||
82 | u32 fuse_shift_ft_mask, fuse_shift_ft_shift; | ||
83 | u32 fuse_spare_realignment; | ||
84 | }; | ||
85 | |||
86 | struct tsensor_shared_calib { | ||
87 | u32 base_cp, base_ft; | ||
88 | u32 actual_temp_cp, actual_temp_ft; | ||
89 | }; | ||
90 | |||
91 | struct tegra_soctherm_soc { | ||
92 | const struct tegra_tsensor *tsensors; | ||
93 | const unsigned int num_tsensors; | ||
94 | const struct tegra_tsensor_group **ttgs; | ||
95 | const unsigned int num_ttgs; | ||
96 | const struct tegra_soctherm_fuse *tfuse; | ||
97 | }; | ||
98 | |||
99 | int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse, | ||
100 | struct tsensor_shared_calib *shared); | ||
101 | int tegra_calc_tsensor_calib(const struct tegra_tsensor *sensor, | ||
102 | const struct tsensor_shared_calib *shared, | ||
103 | u32 *calib); | ||
104 | |||
105 | #ifdef CONFIG_ARCH_TEGRA_124_SOC | ||
106 | extern const struct tegra_soctherm_soc tegra124_soctherm; | ||
107 | #endif | ||
108 | |||
109 | #endif | ||
110 | |||
diff --git a/drivers/thermal/tegra/tegra-soctherm.c b/drivers/thermal/tegra/tegra-soctherm.c deleted file mode 100644 index e486d034adb4..000000000000 --- a/drivers/thermal/tegra/tegra-soctherm.c +++ /dev/null | |||
@@ -1,565 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * Author: | ||
5 | * Mikko Perttunen <mperttunen@nvidia.com> | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/bitops.h> | ||
19 | #include <linux/clk.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/err.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/of.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/reset.h> | ||
28 | #include <linux/thermal.h> | ||
29 | |||
30 | #include <soc/tegra/fuse.h> | ||
31 | #include <dt-bindings/thermal/tegra124-soctherm.h> | ||
32 | |||
33 | #define SENSOR_CONFIG0 0 | ||
34 | #define SENSOR_CONFIG0_STOP BIT(0) | ||
35 | #define SENSOR_CONFIG0_TALL_SHIFT 8 | ||
36 | #define SENSOR_CONFIG0_TCALC_OVER BIT(4) | ||
37 | #define SENSOR_CONFIG0_OVER BIT(3) | ||
38 | #define SENSOR_CONFIG0_CPTR_OVER BIT(2) | ||
39 | |||
40 | #define SENSOR_CONFIG1 4 | ||
41 | #define SENSOR_CONFIG1_TSAMPLE_SHIFT 0 | ||
42 | #define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15 | ||
43 | #define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24 | ||
44 | #define SENSOR_CONFIG1_TEMP_ENABLE BIT(31) | ||
45 | |||
46 | #define SENSOR_CONFIG2 8 | ||
47 | #define SENSOR_CONFIG2_THERMA_SHIFT 16 | ||
48 | #define SENSOR_CONFIG2_THERMB_SHIFT 0 | ||
49 | |||
50 | #define SENSOR_PDIV 0x1c0 | ||
51 | #define SENSOR_PDIV_CPU_MASK (0xf << 12) | ||
52 | #define SENSOR_PDIV_GPU_MASK (0xf << 8) | ||
53 | #define SENSOR_PDIV_MEM_MASK (0xf << 4) | ||
54 | #define SENSOR_PDIV_PLLX_MASK (0xf << 0) | ||
55 | |||
56 | #define SENSOR_HOTSPOT_OFF 0x1c4 | ||
57 | #define SENSOR_HOTSPOT_CPU_MASK (0xff << 16) | ||
58 | #define SENSOR_HOTSPOT_GPU_MASK (0xff << 8) | ||
59 | #define SENSOR_HOTSPOT_MEM_MASK (0xff << 0) | ||
60 | |||
61 | #define SENSOR_TEMP1 0x1c8 | ||
62 | #define SENSOR_TEMP1_CPU_TEMP_MASK (0xffff << 16) | ||
63 | #define SENSOR_TEMP1_GPU_TEMP_MASK 0xffff | ||
64 | #define SENSOR_TEMP2 0x1cc | ||
65 | #define SENSOR_TEMP2_MEM_TEMP_MASK (0xffff << 16) | ||
66 | #define SENSOR_TEMP2_PLLX_TEMP_MASK 0xffff | ||
67 | |||
68 | #define READBACK_VALUE_MASK 0xff00 | ||
69 | #define READBACK_VALUE_SHIFT 8 | ||
70 | #define READBACK_ADD_HALF BIT(7) | ||
71 | #define READBACK_NEGATE BIT(0) | ||
72 | |||
73 | #define FUSE_TSENSOR8_CALIB 0x180 | ||
74 | #define FUSE_SPARE_REALIGNMENT_REG_0 0x1fc | ||
75 | |||
76 | #define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff | ||
77 | #define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13) | ||
78 | #define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13 | ||
79 | |||
80 | #define FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK 0x3ff | ||
81 | #define FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK (0x7ff << 10) | ||
82 | #define FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT 10 | ||
83 | |||
84 | #define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_MASK 0x3f | ||
85 | #define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK (0x1f << 21) | ||
86 | #define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 21 | ||
87 | |||
88 | #define NOMINAL_CALIB_FT_T124 105 | ||
89 | #define NOMINAL_CALIB_CP_T124 25 | ||
90 | |||
91 | /* get val from register(r) mask bits(m) */ | ||
92 | #define REG_GET_MASK(r, m) (((r) & (m)) >> (ffs(m) - 1)) | ||
93 | /* set val(v) to mask bits(m) of register(r) */ | ||
94 | #define REG_SET_MASK(r, m, v) (((r) & ~(m)) | \ | ||
95 | (((v) & (m >> (ffs(m) - 1))) << (ffs(m) - 1))) | ||
96 | |||
97 | /** | ||
98 | * struct tegra_tsensor_group - SOC_THERM sensor group data | ||
99 | * @name: short name of the temperature sensor group | ||
100 | * @id: numeric ID of the temperature sensor group | ||
101 | * @sensor_temp_offset: offset of the SENSOR_TEMP* register | ||
102 | * @sensor_temp_mask: bit mask for this sensor group in SENSOR_TEMP* register | ||
103 | * @pdiv: the sensor count post-divider to use during runtime | ||
104 | * @pdiv_ate: the sensor count post-divider used during automated test | ||
105 | * @pdiv_mask: register bitfield mask for the PDIV field for this sensor | ||
106 | * @pllx_hotspot_diff: hotspot offset from the PLLX sensor, must be 0 for | ||
107 | PLLX sensor group | ||
108 | * @pllx_hotspot_mask: register bitfield mask for the HOTSPOT field | ||
109 | */ | ||
110 | struct tegra_tsensor_group { | ||
111 | const char *name; | ||
112 | u8 id; | ||
113 | u16 sensor_temp_offset; | ||
114 | u32 sensor_temp_mask; | ||
115 | u32 pdiv, pdiv_ate, pdiv_mask; | ||
116 | u32 pllx_hotspot_diff, pllx_hotspot_mask; | ||
117 | }; | ||
118 | |||
119 | struct tegra_tsensor_configuration { | ||
120 | u32 tall, tiddq_en, ten_count, tsample, tsample_ate; | ||
121 | }; | ||
122 | |||
123 | struct tegra_tsensor { | ||
124 | const struct tegra_tsensor_configuration *config; | ||
125 | u32 base, calib_fuse_offset; | ||
126 | /* Correction values used to modify values read from calibration fuses */ | ||
127 | s32 fuse_corr_alpha, fuse_corr_beta; | ||
128 | const struct tegra_tsensor_group *group; | ||
129 | }; | ||
130 | |||
131 | struct tegra_thermctl_zone { | ||
132 | void __iomem *reg; | ||
133 | u32 mask; | ||
134 | }; | ||
135 | |||
136 | static const struct tegra_tsensor_configuration t124_tsensor_config = { | ||
137 | .tall = 16300, | ||
138 | .tiddq_en = 1, | ||
139 | .ten_count = 1, | ||
140 | .tsample = 120, | ||
141 | .tsample_ate = 480, | ||
142 | }; | ||
143 | |||
144 | static const struct tegra_tsensor_group tegra124_tsensor_group_cpu = { | ||
145 | .id = TEGRA124_SOCTHERM_SENSOR_CPU, | ||
146 | .name = "cpu", | ||
147 | .sensor_temp_offset = SENSOR_TEMP1, | ||
148 | .sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK, | ||
149 | .pdiv = 8, | ||
150 | .pdiv_ate = 8, | ||
151 | .pdiv_mask = SENSOR_PDIV_CPU_MASK, | ||
152 | .pllx_hotspot_diff = 10, | ||
153 | .pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK, | ||
154 | }; | ||
155 | |||
156 | static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = { | ||
157 | .id = TEGRA124_SOCTHERM_SENSOR_GPU, | ||
158 | .name = "gpu", | ||
159 | .sensor_temp_offset = SENSOR_TEMP1, | ||
160 | .sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK, | ||
161 | .pdiv = 8, | ||
162 | .pdiv_ate = 8, | ||
163 | .pdiv_mask = SENSOR_PDIV_GPU_MASK, | ||
164 | .pllx_hotspot_diff = 5, | ||
165 | .pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK, | ||
166 | }; | ||
167 | |||
168 | static const struct tegra_tsensor_group tegra124_tsensor_group_pll = { | ||
169 | .id = TEGRA124_SOCTHERM_SENSOR_PLLX, | ||
170 | .name = "pll", | ||
171 | .sensor_temp_offset = SENSOR_TEMP2, | ||
172 | .sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK, | ||
173 | .pdiv = 8, | ||
174 | .pdiv_ate = 8, | ||
175 | .pdiv_mask = SENSOR_PDIV_PLLX_MASK, | ||
176 | .pllx_hotspot_diff = 0, | ||
177 | .pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK, | ||
178 | }; | ||
179 | |||
180 | static const struct tegra_tsensor_group tegra124_tsensor_group_mem = { | ||
181 | .id = TEGRA124_SOCTHERM_SENSOR_MEM, | ||
182 | .name = "mem", | ||
183 | .sensor_temp_offset = SENSOR_TEMP2, | ||
184 | .sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK, | ||
185 | .pdiv = 8, | ||
186 | .pdiv_ate = 8, | ||
187 | .pdiv_mask = SENSOR_PDIV_MEM_MASK, | ||
188 | }; | ||
189 | |||
190 | static const struct tegra_tsensor_group * | ||
191 | tegra124_tsensor_groups[TEGRA124_SOCTHERM_SENSOR_NUM] = { | ||
192 | &tegra124_tsensor_group_cpu, | ||
193 | &tegra124_tsensor_group_gpu, | ||
194 | &tegra124_tsensor_group_pll, | ||
195 | &tegra124_tsensor_group_mem, | ||
196 | }; | ||
197 | |||
198 | static const struct tegra_tsensor t124_tsensors[] = { | ||
199 | { | ||
200 | .config = &t124_tsensor_config, | ||
201 | .base = 0xc0, | ||
202 | .calib_fuse_offset = 0x098, | ||
203 | .fuse_corr_alpha = 1135400, | ||
204 | .fuse_corr_beta = -6266900, | ||
205 | .group = &tegra124_tsensor_group_cpu, | ||
206 | }, | ||
207 | { | ||
208 | .config = &t124_tsensor_config, | ||
209 | .base = 0xe0, | ||
210 | .calib_fuse_offset = 0x084, | ||
211 | .fuse_corr_alpha = 1122220, | ||
212 | .fuse_corr_beta = -5700700, | ||
213 | .group = &tegra124_tsensor_group_cpu, | ||
214 | }, | ||
215 | { | ||
216 | .config = &t124_tsensor_config, | ||
217 | .base = 0x100, | ||
218 | .calib_fuse_offset = 0x088, | ||
219 | .fuse_corr_alpha = 1127000, | ||
220 | .fuse_corr_beta = -6768200, | ||
221 | .group = &tegra124_tsensor_group_cpu, | ||
222 | }, | ||
223 | { | ||
224 | .config = &t124_tsensor_config, | ||
225 | .base = 0x120, | ||
226 | .calib_fuse_offset = 0x12c, | ||
227 | .fuse_corr_alpha = 1110900, | ||
228 | .fuse_corr_beta = -6232000, | ||
229 | .group = &tegra124_tsensor_group_cpu, | ||
230 | }, | ||
231 | { | ||
232 | .config = &t124_tsensor_config, | ||
233 | .base = 0x140, | ||
234 | .calib_fuse_offset = 0x158, | ||
235 | .fuse_corr_alpha = 1122300, | ||
236 | .fuse_corr_beta = -5936400, | ||
237 | .group = &tegra124_tsensor_group_mem, | ||
238 | }, | ||
239 | { | ||
240 | .config = &t124_tsensor_config, | ||
241 | .base = 0x160, | ||
242 | .calib_fuse_offset = 0x15c, | ||
243 | .fuse_corr_alpha = 1145700, | ||
244 | .fuse_corr_beta = -7124600, | ||
245 | .group = &tegra124_tsensor_group_mem, | ||
246 | }, | ||
247 | { | ||
248 | .config = &t124_tsensor_config, | ||
249 | .base = 0x180, | ||
250 | .calib_fuse_offset = 0x154, | ||
251 | .fuse_corr_alpha = 1120100, | ||
252 | .fuse_corr_beta = -6000500, | ||
253 | .group = &tegra124_tsensor_group_gpu, | ||
254 | }, | ||
255 | { | ||
256 | .config = &t124_tsensor_config, | ||
257 | .base = 0x1a0, | ||
258 | .calib_fuse_offset = 0x160, | ||
259 | .fuse_corr_alpha = 1106500, | ||
260 | .fuse_corr_beta = -6729300, | ||
261 | .group = &tegra124_tsensor_group_pll, | ||
262 | }, | ||
263 | }; | ||
264 | |||
265 | struct tegra_soctherm { | ||
266 | struct reset_control *reset; | ||
267 | struct clk *clock_tsensor; | ||
268 | struct clk *clock_soctherm; | ||
269 | void __iomem *regs; | ||
270 | }; | ||
271 | |||
272 | struct tsensor_shared_calibration { | ||
273 | u32 base_cp, base_ft; | ||
274 | u32 actual_temp_cp, actual_temp_ft; | ||
275 | }; | ||
276 | |||
277 | static int calculate_shared_calibration(struct tsensor_shared_calibration *r) | ||
278 | { | ||
279 | u32 val, shifted_cp, shifted_ft; | ||
280 | int err; | ||
281 | |||
282 | err = tegra_fuse_readl(FUSE_TSENSOR8_CALIB, &val); | ||
283 | if (err) | ||
284 | return err; | ||
285 | r->base_cp = val & FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK; | ||
286 | r->base_ft = (val & FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK) | ||
287 | >> FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT; | ||
288 | val = ((val & FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK) | ||
289 | >> FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT); | ||
290 | shifted_ft = sign_extend32(val, 4); | ||
291 | |||
292 | err = tegra_fuse_readl(FUSE_SPARE_REALIGNMENT_REG_0, &val); | ||
293 | if (err) | ||
294 | return err; | ||
295 | shifted_cp = sign_extend32(val, 5); | ||
296 | |||
297 | r->actual_temp_cp = 2 * NOMINAL_CALIB_CP_T124 + shifted_cp; | ||
298 | r->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + shifted_ft; | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static s64 div64_s64_precise(s64 a, s64 b) | ||
304 | { | ||
305 | s64 r, al; | ||
306 | |||
307 | /* Scale up for increased precision division */ | ||
308 | al = a << 16; | ||
309 | |||
310 | r = div64_s64(al * 2 + 1, 2 * b); | ||
311 | return r >> 16; | ||
312 | } | ||
313 | |||
314 | static int | ||
315 | calculate_tsensor_calibration(const struct tegra_tsensor *sensor, | ||
316 | const struct tsensor_shared_calibration *shared, | ||
317 | u32 *calib) | ||
318 | { | ||
319 | u32 val; | ||
320 | s32 actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp, | ||
321 | mult, div; | ||
322 | s16 therma, thermb; | ||
323 | s64 tmp; | ||
324 | int err; | ||
325 | |||
326 | err = tegra_fuse_readl(sensor->calib_fuse_offset, &val); | ||
327 | if (err) | ||
328 | return err; | ||
329 | |||
330 | actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12); | ||
331 | val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK) | ||
332 | >> FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT; | ||
333 | actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12); | ||
334 | |||
335 | delta_sens = actual_tsensor_ft - actual_tsensor_cp; | ||
336 | delta_temp = shared->actual_temp_ft - shared->actual_temp_cp; | ||
337 | |||
338 | mult = sensor->group->pdiv * sensor->config->tsample_ate; | ||
339 | div = sensor->config->tsample * sensor->group->pdiv_ate; | ||
340 | |||
341 | therma = div64_s64_precise((s64) delta_temp * (1LL << 13) * mult, | ||
342 | (s64) delta_sens * div); | ||
343 | |||
344 | tmp = (s64)actual_tsensor_ft * shared->actual_temp_cp - | ||
345 | (s64)actual_tsensor_cp * shared->actual_temp_ft; | ||
346 | thermb = div64_s64_precise(tmp, (s64)delta_sens); | ||
347 | |||
348 | therma = div64_s64_precise((s64)therma * sensor->fuse_corr_alpha, | ||
349 | (s64)1000000LL); | ||
350 | thermb = div64_s64_precise((s64)thermb * sensor->fuse_corr_alpha + | ||
351 | sensor->fuse_corr_beta, (s64)1000000LL); | ||
352 | |||
353 | *calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) | | ||
354 | ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT); | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static int enable_tsensor(struct tegra_soctherm *tegra, | ||
360 | const struct tegra_tsensor *sensor, | ||
361 | const struct tsensor_shared_calibration *shared) | ||
362 | { | ||
363 | void __iomem *base = tegra->regs + sensor->base; | ||
364 | unsigned int val; | ||
365 | u32 calib; | ||
366 | int err; | ||
367 | |||
368 | err = calculate_tsensor_calibration(sensor, shared, &calib); | ||
369 | if (err) | ||
370 | return err; | ||
371 | |||
372 | val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT; | ||
373 | writel(val, base + SENSOR_CONFIG0); | ||
374 | |||
375 | val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT; | ||
376 | val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT; | ||
377 | val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT; | ||
378 | val |= SENSOR_CONFIG1_TEMP_ENABLE; | ||
379 | writel(val, base + SENSOR_CONFIG1); | ||
380 | |||
381 | writel(calib, base + SENSOR_CONFIG2); | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * Translate from soctherm readback format to millicelsius. | ||
388 | * The soctherm readback format in bits is as follows: | ||
389 | * TTTTTTTT H______N | ||
390 | * where T's contain the temperature in Celsius, | ||
391 | * H denotes an addition of 0.5 Celsius and N denotes negation | ||
392 | * of the final value. | ||
393 | */ | ||
394 | static int translate_temp(u16 val) | ||
395 | { | ||
396 | long t; | ||
397 | |||
398 | t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000; | ||
399 | if (val & READBACK_ADD_HALF) | ||
400 | t += 500; | ||
401 | if (val & READBACK_NEGATE) | ||
402 | t *= -1; | ||
403 | |||
404 | return t; | ||
405 | } | ||
406 | |||
407 | static int tegra_thermctl_get_temp(void *data, int *out_temp) | ||
408 | { | ||
409 | struct tegra_thermctl_zone *zone = data; | ||
410 | u32 val; | ||
411 | |||
412 | val = readl(zone->reg); | ||
413 | val = REG_GET_MASK(val, zone->mask); | ||
414 | *out_temp = translate_temp(val); | ||
415 | |||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = { | ||
420 | .get_temp = tegra_thermctl_get_temp, | ||
421 | }; | ||
422 | |||
423 | static const struct of_device_id tegra_soctherm_of_match[] = { | ||
424 | { .compatible = "nvidia,tegra124-soctherm" }, | ||
425 | { }, | ||
426 | }; | ||
427 | MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match); | ||
428 | |||
429 | static int tegra_soctherm_probe(struct platform_device *pdev) | ||
430 | { | ||
431 | struct tegra_soctherm *tegra; | ||
432 | struct thermal_zone_device *z; | ||
433 | struct tsensor_shared_calibration shared_calib; | ||
434 | struct resource *res; | ||
435 | unsigned int i; | ||
436 | int err; | ||
437 | u32 pdiv, hotspot; | ||
438 | |||
439 | const struct tegra_tsensor *tsensors = t124_tsensors; | ||
440 | const struct tegra_tsensor_group **ttgs = tegra124_tsensor_groups; | ||
441 | |||
442 | tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); | ||
443 | if (!tegra) | ||
444 | return -ENOMEM; | ||
445 | |||
446 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
447 | tegra->regs = devm_ioremap_resource(&pdev->dev, res); | ||
448 | if (IS_ERR(tegra->regs)) | ||
449 | return PTR_ERR(tegra->regs); | ||
450 | |||
451 | tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm"); | ||
452 | if (IS_ERR(tegra->reset)) { | ||
453 | dev_err(&pdev->dev, "can't get soctherm reset\n"); | ||
454 | return PTR_ERR(tegra->reset); | ||
455 | } | ||
456 | |||
457 | tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor"); | ||
458 | if (IS_ERR(tegra->clock_tsensor)) { | ||
459 | dev_err(&pdev->dev, "can't get tsensor clock\n"); | ||
460 | return PTR_ERR(tegra->clock_tsensor); | ||
461 | } | ||
462 | |||
463 | tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm"); | ||
464 | if (IS_ERR(tegra->clock_soctherm)) { | ||
465 | dev_err(&pdev->dev, "can't get soctherm clock\n"); | ||
466 | return PTR_ERR(tegra->clock_soctherm); | ||
467 | } | ||
468 | |||
469 | reset_control_assert(tegra->reset); | ||
470 | |||
471 | err = clk_prepare_enable(tegra->clock_soctherm); | ||
472 | if (err) | ||
473 | return err; | ||
474 | |||
475 | err = clk_prepare_enable(tegra->clock_tsensor); | ||
476 | if (err) { | ||
477 | clk_disable_unprepare(tegra->clock_soctherm); | ||
478 | return err; | ||
479 | } | ||
480 | |||
481 | reset_control_deassert(tegra->reset); | ||
482 | |||
483 | /* Initialize raw sensors */ | ||
484 | |||
485 | err = calculate_shared_calibration(&shared_calib); | ||
486 | if (err) | ||
487 | goto disable_clocks; | ||
488 | |||
489 | for (i = 0; i < ARRAY_SIZE(t124_tsensors); ++i) { | ||
490 | err = enable_tsensor(tegra, tsensors + i, &shared_calib); | ||
491 | if (err) | ||
492 | goto disable_clocks; | ||
493 | } | ||
494 | |||
495 | /* Program pdiv and hotspot offsets per THERM */ | ||
496 | pdiv = readl(tegra->regs + SENSOR_PDIV); | ||
497 | hotspot = readl(tegra->regs + SENSOR_HOTSPOT_OFF); | ||
498 | for (i = 0; i < TEGRA124_SOCTHERM_SENSOR_NUM; ++i) { | ||
499 | pdiv = REG_SET_MASK(pdiv, ttgs[i]->pdiv_mask, | ||
500 | ttgs[i]->pdiv); | ||
501 | /* hotspot offset from PLLX, doesn't need to configure PLLX */ | ||
502 | if (ttgs[i]->id != TEGRA124_SOCTHERM_SENSOR_PLLX) | ||
503 | hotspot = REG_SET_MASK(hotspot, | ||
504 | ttgs[i]->pllx_hotspot_mask, | ||
505 | ttgs[i]->pllx_hotspot_diff); | ||
506 | } | ||
507 | writel(pdiv, tegra->regs + SENSOR_PDIV); | ||
508 | writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF); | ||
509 | |||
510 | /* Initialize thermctl sensors */ | ||
511 | |||
512 | for (i = 0; i < TEGRA124_SOCTHERM_SENSOR_NUM; ++i) { | ||
513 | struct tegra_thermctl_zone *zone = | ||
514 | devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL); | ||
515 | if (!zone) { | ||
516 | err = -ENOMEM; | ||
517 | goto disable_clocks; | ||
518 | } | ||
519 | |||
520 | zone->reg = tegra->regs + ttgs[i]->sensor_temp_offset; | ||
521 | zone->mask = ttgs[i]->sensor_temp_mask; | ||
522 | |||
523 | z = devm_thermal_zone_of_sensor_register(&pdev->dev, | ||
524 | ttgs[i]->id, zone, | ||
525 | &tegra_of_thermal_ops); | ||
526 | if (IS_ERR(z)) { | ||
527 | err = PTR_ERR(z); | ||
528 | dev_err(&pdev->dev, "failed to register sensor: %d\n", | ||
529 | err); | ||
530 | goto disable_clocks; | ||
531 | } | ||
532 | } | ||
533 | |||
534 | return 0; | ||
535 | |||
536 | disable_clocks: | ||
537 | clk_disable_unprepare(tegra->clock_tsensor); | ||
538 | clk_disable_unprepare(tegra->clock_soctherm); | ||
539 | |||
540 | return err; | ||
541 | } | ||
542 | |||
543 | static int tegra_soctherm_remove(struct platform_device *pdev) | ||
544 | { | ||
545 | struct tegra_soctherm *tegra = platform_get_drvdata(pdev); | ||
546 | |||
547 | clk_disable_unprepare(tegra->clock_tsensor); | ||
548 | clk_disable_unprepare(tegra->clock_soctherm); | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | static struct platform_driver tegra_soctherm_driver = { | ||
554 | .probe = tegra_soctherm_probe, | ||
555 | .remove = tegra_soctherm_remove, | ||
556 | .driver = { | ||
557 | .name = "tegra-soctherm", | ||
558 | .of_match_table = tegra_soctherm_of_match, | ||
559 | }, | ||
560 | }; | ||
561 | module_platform_driver(tegra_soctherm_driver); | ||
562 | |||
563 | MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); | ||
564 | MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver"); | ||
565 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/thermal/tegra/tegra124-soctherm.c b/drivers/thermal/tegra/tegra124-soctherm.c new file mode 100644 index 000000000000..06aad13a979f --- /dev/null +++ b/drivers/thermal/tegra/tegra124-soctherm.c | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | |||
18 | #include <dt-bindings/thermal/tegra124-soctherm.h> | ||
19 | |||
20 | #include "soctherm.h" | ||
21 | |||
22 | static const struct tegra_tsensor_configuration tegra124_tsensor_config = { | ||
23 | .tall = 16300, | ||
24 | .tiddq_en = 1, | ||
25 | .ten_count = 1, | ||
26 | .tsample = 120, | ||
27 | .tsample_ate = 480, | ||
28 | }; | ||
29 | |||
30 | static const struct tegra_tsensor_group tegra124_tsensor_group_cpu = { | ||
31 | .id = TEGRA124_SOCTHERM_SENSOR_CPU, | ||
32 | .name = "cpu", | ||
33 | .sensor_temp_offset = SENSOR_TEMP1, | ||
34 | .sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK, | ||
35 | .pdiv = 8, | ||
36 | .pdiv_ate = 8, | ||
37 | .pdiv_mask = SENSOR_PDIV_CPU_MASK, | ||
38 | .pllx_hotspot_diff = 10, | ||
39 | .pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK, | ||
40 | }; | ||
41 | |||
42 | static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = { | ||
43 | .id = TEGRA124_SOCTHERM_SENSOR_GPU, | ||
44 | .name = "gpu", | ||
45 | .sensor_temp_offset = SENSOR_TEMP1, | ||
46 | .sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK, | ||
47 | .pdiv = 8, | ||
48 | .pdiv_ate = 8, | ||
49 | .pdiv_mask = SENSOR_PDIV_GPU_MASK, | ||
50 | .pllx_hotspot_diff = 5, | ||
51 | .pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK, | ||
52 | }; | ||
53 | |||
54 | static const struct tegra_tsensor_group tegra124_tsensor_group_pll = { | ||
55 | .id = TEGRA124_SOCTHERM_SENSOR_PLLX, | ||
56 | .name = "pll", | ||
57 | .sensor_temp_offset = SENSOR_TEMP2, | ||
58 | .sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK, | ||
59 | .pdiv = 8, | ||
60 | .pdiv_ate = 8, | ||
61 | .pdiv_mask = SENSOR_PDIV_PLLX_MASK, | ||
62 | }; | ||
63 | |||
64 | static const struct tegra_tsensor_group tegra124_tsensor_group_mem = { | ||
65 | .id = TEGRA124_SOCTHERM_SENSOR_MEM, | ||
66 | .name = "mem", | ||
67 | .sensor_temp_offset = SENSOR_TEMP2, | ||
68 | .sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK, | ||
69 | .pdiv = 8, | ||
70 | .pdiv_ate = 8, | ||
71 | .pdiv_mask = SENSOR_PDIV_MEM_MASK, | ||
72 | .pllx_hotspot_diff = 0, | ||
73 | .pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK, | ||
74 | }; | ||
75 | |||
76 | static const struct tegra_tsensor_group *tegra124_tsensor_groups[] = { | ||
77 | &tegra124_tsensor_group_cpu, | ||
78 | &tegra124_tsensor_group_gpu, | ||
79 | &tegra124_tsensor_group_pll, | ||
80 | &tegra124_tsensor_group_mem, | ||
81 | }; | ||
82 | |||
83 | static const struct tegra_tsensor tegra124_tsensors[] = { | ||
84 | { | ||
85 | .name = "cpu0", | ||
86 | .base = 0xc0, | ||
87 | .config = &tegra124_tsensor_config, | ||
88 | .calib_fuse_offset = 0x098, | ||
89 | .fuse_corr_alpha = 1135400, | ||
90 | .fuse_corr_beta = -6266900, | ||
91 | .group = &tegra124_tsensor_group_cpu, | ||
92 | }, { | ||
93 | .name = "cpu1", | ||
94 | .base = 0xe0, | ||
95 | .config = &tegra124_tsensor_config, | ||
96 | .calib_fuse_offset = 0x084, | ||
97 | .fuse_corr_alpha = 1122220, | ||
98 | .fuse_corr_beta = -5700700, | ||
99 | .group = &tegra124_tsensor_group_cpu, | ||
100 | }, { | ||
101 | .name = "cpu2", | ||
102 | .base = 0x100, | ||
103 | .config = &tegra124_tsensor_config, | ||
104 | .calib_fuse_offset = 0x088, | ||
105 | .fuse_corr_alpha = 1127000, | ||
106 | .fuse_corr_beta = -6768200, | ||
107 | .group = &tegra124_tsensor_group_cpu, | ||
108 | }, { | ||
109 | .name = "cpu3", | ||
110 | .base = 0x120, | ||
111 | .config = &tegra124_tsensor_config, | ||
112 | .calib_fuse_offset = 0x12c, | ||
113 | .fuse_corr_alpha = 1110900, | ||
114 | .fuse_corr_beta = -6232000, | ||
115 | .group = &tegra124_tsensor_group_cpu, | ||
116 | }, { | ||
117 | .name = "mem0", | ||
118 | .base = 0x140, | ||
119 | .config = &tegra124_tsensor_config, | ||
120 | .calib_fuse_offset = 0x158, | ||
121 | .fuse_corr_alpha = 1122300, | ||
122 | .fuse_corr_beta = -5936400, | ||
123 | .group = &tegra124_tsensor_group_mem, | ||
124 | }, { | ||
125 | .name = "mem1", | ||
126 | .base = 0x160, | ||
127 | .config = &tegra124_tsensor_config, | ||
128 | .calib_fuse_offset = 0x15c, | ||
129 | .fuse_corr_alpha = 1145700, | ||
130 | .fuse_corr_beta = -7124600, | ||
131 | .group = &tegra124_tsensor_group_mem, | ||
132 | }, { | ||
133 | .name = "gpu", | ||
134 | .base = 0x180, | ||
135 | .config = &tegra124_tsensor_config, | ||
136 | .calib_fuse_offset = 0x154, | ||
137 | .fuse_corr_alpha = 1120100, | ||
138 | .fuse_corr_beta = -6000500, | ||
139 | .group = &tegra124_tsensor_group_gpu, | ||
140 | }, { | ||
141 | .name = "pllx", | ||
142 | .base = 0x1a0, | ||
143 | .config = &tegra124_tsensor_config, | ||
144 | .calib_fuse_offset = 0x160, | ||
145 | .fuse_corr_alpha = 1106500, | ||
146 | .fuse_corr_beta = -6729300, | ||
147 | .group = &tegra124_tsensor_group_pll, | ||
148 | }, | ||
149 | }; | ||
150 | |||
151 | /* | ||
152 | * Mask/shift bits in FUSE_TSENSOR_COMMON and | ||
153 | * FUSE_TSENSOR_COMMON, which are described in | ||
154 | * tegra_soctherm_fuse.c | ||
155 | */ | ||
156 | static const struct tegra_soctherm_fuse tegra124_soctherm_fuse = { | ||
157 | .fuse_base_cp_mask = 0x3ff, | ||
158 | .fuse_base_cp_shift = 0, | ||
159 | .fuse_base_ft_mask = 0x7ff << 10, | ||
160 | .fuse_base_ft_shift = 10, | ||
161 | .fuse_shift_ft_mask = 0x1f << 21, | ||
162 | .fuse_shift_ft_shift = 21, | ||
163 | .fuse_spare_realignment = 0x1fc, | ||
164 | }; | ||
165 | |||
166 | const struct tegra_soctherm_soc tegra124_soctherm = { | ||
167 | .tsensors = tegra124_tsensors, | ||
168 | .num_tsensors = ARRAY_SIZE(tegra124_tsensors), | ||
169 | .ttgs = tegra124_tsensor_groups, | ||
170 | .num_ttgs = ARRAY_SIZE(tegra124_tsensor_groups), | ||
171 | .tfuse = &tegra124_soctherm_fuse, | ||
172 | }; | ||