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/mod_devicetable.h> |
13 | #include <linux/module.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)) |
35 | #define OTPTSUTRIM_EN_MASK BIT(31) |
36 | #define OTPTSUTRIM_MASK GENMASK(11, 0) |
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) |
50 | #define TS_CODE_CAP_TIMES 8 /* Total number of ADC data samples */ |
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 | |
76 | static int rzg2l_thermal_get_temp(struct thermal_zone_device *tz, int *temp) |
77 | { |
78 | struct rzg2l_thermal_priv *priv = thermal_zone_device_priv(tzd: tz); |
79 | u32 result = 0, dsensor, ts_code_ave; |
80 | int val, i; |
81 | |
82 | for (i = 0; i < TS_CODE_CAP_TIMES ; i++) { |
83 | /* |
84 | * TSU repeats measurement at 20 microseconds intervals and |
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(min: 20, max: 30); |
92 | } |
93 | |
94 | ts_code_ave = result / TS_CODE_CAP_TIMES; |
95 | |
96 | /* |
97 | * Calculate actual sensor value by applying curvature correction formula |
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 | |
104 | /* |
105 | * The temperature Tj is calculated by the formula |
106 | * Tj = (dsensor − calib1) * 165/ (calib0 − calib1) − 40 |
107 | * where calib0 and calib1 are the calibration values. |
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 | |
117 | static const struct thermal_zone_device_ops rzg2l_tz_of_ops = { |
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, data: 0); |
127 | |
128 | /* |
129 | * Before setting the START bit, TSU should be in normal operating |
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(min: 60, max: 80); |
134 | |
135 | reg_val = rzg2l_thermal_read(priv, TSU_ST); |
136 | reg_val |= TSU_ST_START; |
137 | rzg2l_thermal_write(priv, TSU_ST, data: 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(dev: &pdev->dev); |
147 | |
148 | pm_runtime_put(dev: &pdev->dev); |
149 | pm_runtime_disable(dev: &pdev->dev); |
150 | reset_control_assert(rstc: priv->rstc); |
151 | } |
152 | |
153 | static void rzg2l_thermal_remove(struct platform_device *pdev) |
154 | { |
155 | struct rzg2l_thermal_priv *priv = dev_get_drvdata(dev: &pdev->dev); |
156 | |
157 | thermal_remove_hwmon_sysfs(tz: priv->zone); |
158 | rzg2l_thermal_reset_assert_pm_disable_put(pdev); |
159 | } |
160 | |
161 | static int rzg2l_thermal_probe(struct platform_device *pdev) |
162 | { |
163 | struct thermal_zone_device *zone; |
164 | struct rzg2l_thermal_priv *priv; |
165 | struct device *dev = &pdev->dev; |
166 | int ret; |
167 | |
168 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
169 | if (!priv) |
170 | return -ENOMEM; |
171 | |
172 | priv->base = devm_platform_ioremap_resource(pdev, index: 0); |
173 | if (IS_ERR(ptr: priv->base)) |
174 | return PTR_ERR(ptr: priv->base); |
175 | |
176 | priv->dev = dev; |
177 | priv->rstc = devm_reset_control_get_exclusive(dev, NULL); |
178 | if (IS_ERR(ptr: priv->rstc)) |
179 | return dev_err_probe(dev, err: PTR_ERR(ptr: priv->rstc), |
180 | fmt: "failed to get cpg reset" ); |
181 | |
182 | ret = reset_control_deassert(rstc: priv->rstc); |
183 | if (ret) |
184 | return dev_err_probe(dev, err: ret, fmt: "failed to deassert" ); |
185 | |
186 | pm_runtime_enable(dev); |
187 | pm_runtime_get_sync(dev); |
188 | |
189 | priv->calib0 = rzg2l_thermal_read(priv, OTPTSUTRIM_REG(0)); |
190 | if (priv->calib0 & OTPTSUTRIM_EN_MASK) |
191 | priv->calib0 &= OTPTSUTRIM_MASK; |
192 | else |
193 | priv->calib0 = SW_CALIB0_VAL; |
194 | |
195 | priv->calib1 = rzg2l_thermal_read(priv, OTPTSUTRIM_REG(1)); |
196 | if (priv->calib1 & OTPTSUTRIM_EN_MASK) |
197 | priv->calib1 &= OTPTSUTRIM_MASK; |
198 | else |
199 | priv->calib1 = SW_CALIB1_VAL; |
200 | |
201 | platform_set_drvdata(pdev, data: priv); |
202 | ret = rzg2l_thermal_init(priv); |
203 | if (ret) { |
204 | dev_err(dev, "Failed to start TSU" ); |
205 | goto err; |
206 | } |
207 | |
208 | zone = devm_thermal_of_zone_register(dev, id: 0, data: priv, |
209 | ops: &rzg2l_tz_of_ops); |
210 | if (IS_ERR(ptr: zone)) { |
211 | dev_err(dev, "Can't register thermal zone" ); |
212 | ret = PTR_ERR(ptr: zone); |
213 | goto err; |
214 | } |
215 | |
216 | priv->zone = zone; |
217 | ret = thermal_add_hwmon_sysfs(tz: priv->zone); |
218 | if (ret) |
219 | goto err; |
220 | |
221 | dev_dbg(dev, "TSU probed with %s calibration values" , |
222 | rzg2l_thermal_read(priv, OTPTSUTRIM_REG(0)) ? "hw" : "sw" ); |
223 | |
224 | return 0; |
225 | |
226 | err: |
227 | rzg2l_thermal_reset_assert_pm_disable_put(pdev); |
228 | return ret; |
229 | } |
230 | |
231 | static const struct of_device_id rzg2l_thermal_dt_ids[] = { |
232 | { .compatible = "renesas,rzg2l-tsu" , }, |
233 | { /* sentinel */ } |
234 | }; |
235 | MODULE_DEVICE_TABLE(of, rzg2l_thermal_dt_ids); |
236 | |
237 | static struct platform_driver rzg2l_thermal_driver = { |
238 | .driver = { |
239 | .name = "rzg2l_thermal" , |
240 | .of_match_table = rzg2l_thermal_dt_ids, |
241 | }, |
242 | .probe = rzg2l_thermal_probe, |
243 | .remove_new = rzg2l_thermal_remove, |
244 | }; |
245 | module_platform_driver(rzg2l_thermal_driver); |
246 | |
247 | MODULE_DESCRIPTION("Renesas RZ/G2L TSU Thermal Sensor Driver" ); |
248 | MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>" ); |
249 | MODULE_LICENSE("GPL v2" ); |
250 | |