Commit | Line | Data |
---|---|---|
673c68bd BD |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Renesas RZ/G2L TSU Thermal Sensor Driver | |
4 | * | |
5 | * Copyright (C) 2021 Renesas Electronics Corporation | |
6 | */ | |
7 | #include <linux/delay.h> | |
8 | #include <linux/err.h> | |
9 | #include <linux/io.h> | |
10 | #include <linux/iopoll.h> | |
11 | #include <linux/math.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/of_device.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/pm_runtime.h> | |
16 | #include <linux/reset.h> | |
17 | #include <linux/thermal.h> | |
18 | #include <linux/units.h> | |
19 | ||
20 | #include "thermal_hwmon.h" | |
21 | ||
22 | #define CTEMP_MASK 0xFFF | |
23 | ||
24 | /* default calibration values, if FUSE values are missing */ | |
25 | #define SW_CALIB0_VAL 3148 | |
26 | #define SW_CALIB1_VAL 503 | |
27 | ||
28 | /* Register offsets */ | |
29 | #define TSU_SM 0x00 | |
30 | #define TSU_ST 0x04 | |
31 | #define TSU_SAD 0x0C | |
32 | #define TSU_SS 0x10 | |
33 | ||
34 | #define OTPTSUTRIM_REG(n) (0x18 + ((n) * 0x4)) | |
2d37f5c9 BD |
35 | #define OTPTSUTRIM_EN_MASK BIT(31) |
36 | #define OTPTSUTRIM_MASK GENMASK(11, 0) | |
673c68bd BD |
37 | |
38 | /* Sensor Mode Register(TSU_SM) */ | |
39 | #define TSU_SM_EN_TS BIT(0) | |
40 | #define TSU_SM_ADC_EN_TS BIT(1) | |
41 | #define TSU_SM_NORMAL_MODE (TSU_SM_EN_TS | TSU_SM_ADC_EN_TS) | |
42 | ||
43 | /* TSU_ST bits */ | |
44 | #define TSU_ST_START BIT(0) | |
45 | ||
46 | #define TSU_SS_CONV_RUNNING BIT(0) | |
47 | ||
48 | #define TS_CODE_AVE_SCALE(x) ((x) * 1000000) | |
49 | #define MCELSIUS(temp) ((temp) * MILLIDEGREE_PER_DEGREE) | |
8f5a9371 | 50 | #define TS_CODE_CAP_TIMES 8 /* Total number of ADC data samples */ |
673c68bd BD |
51 | |
52 | #define RZG2L_THERMAL_GRAN 500 /* milli Celsius */ | |
53 | #define RZG2L_TSU_SS_TIMEOUT_US 1000 | |
54 | ||
55 | #define CURVATURE_CORRECTION_CONST 13 | |
56 | ||
57 | struct rzg2l_thermal_priv { | |
58 | struct device *dev; | |
59 | void __iomem *base; | |
60 | struct thermal_zone_device *zone; | |
61 | struct reset_control *rstc; | |
62 | u32 calib0, calib1; | |
63 | }; | |
64 | ||
65 | static inline u32 rzg2l_thermal_read(struct rzg2l_thermal_priv *priv, u32 reg) | |
66 | { | |
67 | return ioread32(priv->base + reg); | |
68 | } | |
69 | ||
70 | static inline void rzg2l_thermal_write(struct rzg2l_thermal_priv *priv, u32 reg, | |
71 | u32 data) | |
72 | { | |
73 | iowrite32(data, priv->base + reg); | |
74 | } | |
75 | ||
396cbbc6 | 76 | static int rzg2l_thermal_get_temp(struct thermal_zone_device *tz, int *temp) |
673c68bd | 77 | { |
396cbbc6 | 78 | struct rzg2l_thermal_priv *priv = tz->devdata; |
673c68bd BD |
79 | u32 result = 0, dsensor, ts_code_ave; |
80 | int val, i; | |
81 | ||
82 | for (i = 0; i < TS_CODE_CAP_TIMES ; i++) { | |
8f5a9371 BD |
83 | /* |
84 | * TSU repeats measurement at 20 microseconds intervals and | |
673c68bd BD |
85 | * automatically updates the results of measurement. As per |
86 | * the HW manual for measuring temperature we need to read 8 | |
87 | * values consecutively and then take the average. | |
88 | * ts_code_ave = (ts_code[0] + ⋯ + ts_code[7]) / 8 | |
89 | */ | |
90 | result += rzg2l_thermal_read(priv, TSU_SAD) & CTEMP_MASK; | |
91 | usleep_range(20, 30); | |
92 | } | |
93 | ||
94 | ts_code_ave = result / TS_CODE_CAP_TIMES; | |
95 | ||
8f5a9371 BD |
96 | /* |
97 | * Calculate actual sensor value by applying curvature correction formula | |
673c68bd BD |
98 | * dsensor = ts_code_ave / (1 + ts_code_ave * 0.000013). Here we are doing |
99 | * integer calculation by scaling all the values by 1000000. | |
100 | */ | |
101 | dsensor = TS_CODE_AVE_SCALE(ts_code_ave) / | |
102 | (TS_CODE_AVE_SCALE(1) + (ts_code_ave * CURVATURE_CORRECTION_CONST)); | |
103 | ||
8f5a9371 BD |
104 | /* |
105 | * The temperature Tj is calculated by the formula | |
673c68bd | 106 | * Tj = (dsensor − calib1) * 165/ (calib0 − calib1) − 40 |
8f5a9371 | 107 | * where calib0 and calib1 are the calibration values. |
673c68bd BD |
108 | */ |
109 | val = ((dsensor - priv->calib1) * (MCELSIUS(165) / | |
110 | (priv->calib0 - priv->calib1))) - MCELSIUS(40); | |
111 | ||
112 | *temp = roundup(val, RZG2L_THERMAL_GRAN); | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
396cbbc6 | 117 | static const struct thermal_zone_device_ops rzg2l_tz_of_ops = { |
673c68bd BD |
118 | .get_temp = rzg2l_thermal_get_temp, |
119 | }; | |
120 | ||
121 | static int rzg2l_thermal_init(struct rzg2l_thermal_priv *priv) | |
122 | { | |
123 | u32 reg_val; | |
124 | ||
125 | rzg2l_thermal_write(priv, TSU_SM, TSU_SM_NORMAL_MODE); | |
126 | rzg2l_thermal_write(priv, TSU_ST, 0); | |
127 | ||
8f5a9371 BD |
128 | /* |
129 | * Before setting the START bit, TSU should be in normal operating | |
673c68bd BD |
130 | * mode. As per the HW manual, it will take 60 µs to place the TSU |
131 | * into normal operating mode. | |
132 | */ | |
133 | usleep_range(60, 80); | |
134 | ||
135 | reg_val = rzg2l_thermal_read(priv, TSU_ST); | |
136 | reg_val |= TSU_ST_START; | |
137 | rzg2l_thermal_write(priv, TSU_ST, reg_val); | |
138 | ||
139 | return readl_poll_timeout(priv->base + TSU_SS, reg_val, | |
140 | reg_val == TSU_SS_CONV_RUNNING, 50, | |
141 | RZG2L_TSU_SS_TIMEOUT_US); | |
142 | } | |
143 | ||
144 | static void rzg2l_thermal_reset_assert_pm_disable_put(struct platform_device *pdev) | |
145 | { | |
146 | struct rzg2l_thermal_priv *priv = dev_get_drvdata(&pdev->dev); | |
147 | ||
148 | pm_runtime_put(&pdev->dev); | |
149 | pm_runtime_disable(&pdev->dev); | |
150 | reset_control_assert(priv->rstc); | |
151 | } | |
152 | ||
153 | static int rzg2l_thermal_remove(struct platform_device *pdev) | |
154 | { | |
155 | struct rzg2l_thermal_priv *priv = dev_get_drvdata(&pdev->dev); | |
156 | ||
157 | thermal_remove_hwmon_sysfs(priv->zone); | |
158 | rzg2l_thermal_reset_assert_pm_disable_put(pdev); | |
159 | ||
160 | return 0; | |
161 | } | |
162 | ||
163 | static int rzg2l_thermal_probe(struct platform_device *pdev) | |
164 | { | |
165 | struct thermal_zone_device *zone; | |
166 | struct rzg2l_thermal_priv *priv; | |
167 | struct device *dev = &pdev->dev; | |
168 | int ret; | |
169 | ||
170 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
171 | if (!priv) | |
172 | return -ENOMEM; | |
173 | ||
174 | priv->base = devm_platform_ioremap_resource(pdev, 0); | |
175 | if (IS_ERR(priv->base)) | |
176 | return PTR_ERR(priv->base); | |
177 | ||
178 | priv->dev = dev; | |
8ee1c0f6 | 179 | priv->rstc = devm_reset_control_get_exclusive(dev, NULL); |
673c68bd BD |
180 | if (IS_ERR(priv->rstc)) |
181 | return dev_err_probe(dev, PTR_ERR(priv->rstc), | |
182 | "failed to get cpg reset"); | |
183 | ||
8ee1c0f6 BD |
184 | ret = reset_control_deassert(priv->rstc); |
185 | if (ret) | |
186 | return dev_err_probe(dev, ret, "failed to deassert"); | |
673c68bd BD |
187 | |
188 | pm_runtime_enable(dev); | |
189 | pm_runtime_get_sync(dev); | |
190 | ||
191 | priv->calib0 = rzg2l_thermal_read(priv, OTPTSUTRIM_REG(0)); | |
2d37f5c9 BD |
192 | if (priv->calib0 & OTPTSUTRIM_EN_MASK) |
193 | priv->calib0 &= OTPTSUTRIM_MASK; | |
194 | else | |
673c68bd BD |
195 | priv->calib0 = SW_CALIB0_VAL; |
196 | ||
197 | priv->calib1 = rzg2l_thermal_read(priv, OTPTSUTRIM_REG(1)); | |
2d37f5c9 BD |
198 | if (priv->calib1 & OTPTSUTRIM_EN_MASK) |
199 | priv->calib1 &= OTPTSUTRIM_MASK; | |
200 | else | |
673c68bd BD |
201 | priv->calib1 = SW_CALIB1_VAL; |
202 | ||
203 | platform_set_drvdata(pdev, priv); | |
204 | ret = rzg2l_thermal_init(priv); | |
205 | if (ret) { | |
206 | dev_err(dev, "Failed to start TSU"); | |
207 | goto err; | |
208 | } | |
209 | ||
396cbbc6 DL |
210 | zone = devm_thermal_of_zone_register(dev, 0, priv, |
211 | &rzg2l_tz_of_ops); | |
673c68bd BD |
212 | if (IS_ERR(zone)) { |
213 | dev_err(dev, "Can't register thermal zone"); | |
214 | ret = PTR_ERR(zone); | |
215 | goto err; | |
216 | } | |
217 | ||
218 | priv->zone = zone; | |
219 | priv->zone->tzp->no_hwmon = false; | |
220 | ret = thermal_add_hwmon_sysfs(priv->zone); | |
221 | if (ret) | |
222 | goto err; | |
223 | ||
8f5a9371 | 224 | dev_dbg(dev, "TSU probed with %s calibration values", |
673c68bd BD |
225 | rzg2l_thermal_read(priv, OTPTSUTRIM_REG(0)) ? "hw" : "sw"); |
226 | ||
227 | return 0; | |
228 | ||
229 | err: | |
230 | rzg2l_thermal_reset_assert_pm_disable_put(pdev); | |
231 | return ret; | |
232 | } | |
233 | ||
234 | static const struct of_device_id rzg2l_thermal_dt_ids[] = { | |
235 | { .compatible = "renesas,rzg2l-tsu", }, | |
236 | { /* sentinel */ } | |
237 | }; | |
238 | MODULE_DEVICE_TABLE(of, rzg2l_thermal_dt_ids); | |
239 | ||
240 | static struct platform_driver rzg2l_thermal_driver = { | |
241 | .driver = { | |
242 | .name = "rzg2l_thermal", | |
243 | .of_match_table = rzg2l_thermal_dt_ids, | |
244 | }, | |
245 | .probe = rzg2l_thermal_probe, | |
246 | .remove = rzg2l_thermal_remove, | |
247 | }; | |
248 | module_platform_driver(rzg2l_thermal_driver); | |
249 | ||
250 | MODULE_DESCRIPTION("Renesas RZ/G2L TSU Thermal Sensor Driver"); | |
251 | MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); | |
252 | MODULE_LICENSE("GPL v2"); |