1 // SPDX-License-Identifier: GPL-2.0+
3 * Amlogic Thermal Sensor Driver
5 * Copyright (C) 2017 Huan Biao <huan.biao@amlogic.com>
6 * Copyright (C) 2019 Guillaume La Roque <glaroque@baylibre.com>
8 * Register value to celsius temperature formulas:
10 * U = ---------, Uptat = ---------
13 * Temperature = A * ( Uptat + u_efuse / 2^16 )- B
15 * A B m n : calibration parameters
16 * u_efuse : fused calibration value, it's a signed 16 bits value
19 #include <linux/bitfield.h>
20 #include <linux/clk.h>
22 #include <linux/mfd/syscon.h>
23 #include <linux/module.h>
25 #include <linux/platform_device.h>
26 #include <linux/regmap.h>
27 #include <linux/thermal.h>
29 #include "thermal_hwmon.h"
31 #define TSENSOR_CFG_REG1 0x4
32 #define TSENSOR_CFG_REG1_RSET_VBG BIT(12)
33 #define TSENSOR_CFG_REG1_RSET_ADC BIT(11)
34 #define TSENSOR_CFG_REG1_VCM_EN BIT(10)
35 #define TSENSOR_CFG_REG1_VBG_EN BIT(9)
36 #define TSENSOR_CFG_REG1_OUT_CTL BIT(6)
37 #define TSENSOR_CFG_REG1_FILTER_EN BIT(5)
38 #define TSENSOR_CFG_REG1_DEM_EN BIT(3)
39 #define TSENSOR_CFG_REG1_CH_SEL GENMASK(1, 0)
40 #define TSENSOR_CFG_REG1_ENABLE \
41 (TSENSOR_CFG_REG1_FILTER_EN | \
42 TSENSOR_CFG_REG1_VCM_EN | \
43 TSENSOR_CFG_REG1_VBG_EN | \
44 TSENSOR_CFG_REG1_DEM_EN | \
45 TSENSOR_CFG_REG1_CH_SEL)
47 #define TSENSOR_STAT0 0x40
49 #define TSENSOR_STAT9 0x64
51 #define TSENSOR_READ_TEMP_MASK GENMASK(15, 0)
52 #define TSENSOR_TEMP_MASK GENMASK(11, 0)
54 #define TSENSOR_TRIM_SIGN_MASK BIT(15)
55 #define TSENSOR_TRIM_TEMP_MASK GENMASK(14, 0)
56 #define TSENSOR_TRIM_VERSION_MASK GENMASK(31, 24)
58 #define TSENSOR_TRIM_VERSION(_version) \
59 FIELD_GET(TSENSOR_TRIM_VERSION_MASK, _version)
61 #define TSENSOR_TRIM_CALIB_VALID_MASK (GENMASK(3, 2) | BIT(7))
63 #define TSENSOR_CALIB_OFFSET 1
64 #define TSENSOR_CALIB_SHIFT 4
67 * struct amlogic_thermal_soc_calib_data
68 * @A: calibration parameters
69 * @B: calibration parameters
70 * @m: calibration parameters
71 * @n: calibration parameters
73 * This structure is required for configuration of amlogic thermal driver.
75 struct amlogic_thermal_soc_calib_data
{
83 * struct amlogic_thermal_data
84 * @u_efuse_off: register offset to read fused calibration value
85 * @calibration_parameters: calibration parameters structure pointer
86 * @regmap_config: regmap config for the device
87 * This structure is required for configuration of amlogic thermal driver.
89 struct amlogic_thermal_data
{
91 const struct amlogic_thermal_soc_calib_data
*calibration_parameters
;
92 const struct regmap_config
*regmap_config
;
95 struct amlogic_thermal
{
96 struct platform_device
*pdev
;
97 const struct amlogic_thermal_data
*data
;
98 struct regmap
*regmap
;
99 struct regmap
*sec_ao_map
;
101 struct thermal_zone_device
*tzd
;
106 * Calculate a temperature value from a temperature code.
107 * The unit of the temperature is degree milliCelsius.
109 static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal
*pdata
,
112 const struct amlogic_thermal_soc_calib_data
*param
=
113 pdata
->data
->calibration_parameters
;
115 s64 factor
, Uptat
, uefuse
;
117 uefuse
= pdata
->trim_info
& TSENSOR_TRIM_SIGN_MASK
?
118 ~(pdata
->trim_info
& TSENSOR_TRIM_TEMP_MASK
) + 1 :
119 (pdata
->trim_info
& TSENSOR_TRIM_TEMP_MASK
);
121 factor
= param
->n
* temp_code
;
122 factor
= div_s64(factor
, 100);
124 Uptat
= temp_code
* param
->m
;
125 Uptat
= div_s64(Uptat
, 100);
126 Uptat
= Uptat
* BIT(16);
127 Uptat
= div_s64(Uptat
, BIT(16) + factor
);
129 temp
= (Uptat
+ uefuse
) * param
->A
;
130 temp
= div_s64(temp
, BIT(16));
131 temp
= (temp
- param
->B
) * 100;
136 static int amlogic_thermal_initialize(struct amlogic_thermal
*pdata
)
141 regmap_read(pdata
->sec_ao_map
, pdata
->data
->u_efuse_off
,
144 ver
= TSENSOR_TRIM_VERSION(pdata
->trim_info
);
146 if ((ver
& TSENSOR_TRIM_CALIB_VALID_MASK
) == 0) {
148 dev_err(&pdata
->pdev
->dev
,
149 "tsensor thermal calibration not supported: 0x%x!\n",
156 static int amlogic_thermal_enable(struct amlogic_thermal
*data
)
160 ret
= clk_prepare_enable(data
->clk
);
164 regmap_update_bits(data
->regmap
, TSENSOR_CFG_REG1
,
165 TSENSOR_CFG_REG1_ENABLE
, TSENSOR_CFG_REG1_ENABLE
);
170 static void amlogic_thermal_disable(struct amlogic_thermal
*data
)
172 regmap_update_bits(data
->regmap
, TSENSOR_CFG_REG1
,
173 TSENSOR_CFG_REG1_ENABLE
, 0);
174 clk_disable_unprepare(data
->clk
);
177 static int amlogic_thermal_get_temp(struct thermal_zone_device
*tz
, int *temp
)
180 struct amlogic_thermal
*pdata
= thermal_zone_device_priv(tz
);
185 regmap_read(pdata
->regmap
, TSENSOR_STAT0
, &tval
);
187 amlogic_thermal_code_to_millicelsius(pdata
,
188 tval
& TSENSOR_READ_TEMP_MASK
);
193 static const struct thermal_zone_device_ops amlogic_thermal_ops
= {
194 .get_temp
= amlogic_thermal_get_temp
,
197 static const struct regmap_config amlogic_thermal_regmap_config_g12a
= {
201 .max_register
= TSENSOR_STAT9
,
204 static const struct amlogic_thermal_soc_calib_data amlogic_thermal_g12a
= {
211 static const struct amlogic_thermal_data amlogic_thermal_g12a_cpu_param
= {
212 .u_efuse_off
= 0x128,
213 .calibration_parameters
= &amlogic_thermal_g12a
,
214 .regmap_config
= &amlogic_thermal_regmap_config_g12a
,
217 static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param
= {
219 .calibration_parameters
= &amlogic_thermal_g12a
,
220 .regmap_config
= &amlogic_thermal_regmap_config_g12a
,
223 static const struct amlogic_thermal_data amlogic_thermal_a1_cpu_param
= {
224 .u_efuse_off
= 0x114,
225 .calibration_parameters
= &amlogic_thermal_g12a
,
226 .regmap_config
= &amlogic_thermal_regmap_config_g12a
,
229 static const struct of_device_id of_amlogic_thermal_match
[] = {
231 .compatible
= "amlogic,g12a-ddr-thermal",
232 .data
= &amlogic_thermal_g12a_ddr_param
,
235 .compatible
= "amlogic,g12a-cpu-thermal",
236 .data
= &amlogic_thermal_g12a_cpu_param
,
239 .compatible
= "amlogic,a1-cpu-thermal",
240 .data
= &amlogic_thermal_a1_cpu_param
,
244 MODULE_DEVICE_TABLE(of
, of_amlogic_thermal_match
);
246 static int amlogic_thermal_probe(struct platform_device
*pdev
)
248 struct amlogic_thermal
*pdata
;
249 struct device
*dev
= &pdev
->dev
;
253 pdata
= devm_kzalloc(dev
, sizeof(*pdata
), GFP_KERNEL
);
257 pdata
->data
= of_device_get_match_data(dev
);
259 platform_set_drvdata(pdev
, pdata
);
261 base
= devm_platform_ioremap_resource(pdev
, 0);
263 return PTR_ERR(base
);
265 pdata
->regmap
= devm_regmap_init_mmio(dev
, base
,
266 pdata
->data
->regmap_config
);
267 if (IS_ERR(pdata
->regmap
))
268 return PTR_ERR(pdata
->regmap
);
270 pdata
->clk
= devm_clk_get(dev
, NULL
);
271 if (IS_ERR(pdata
->clk
))
272 return dev_err_probe(dev
, PTR_ERR(pdata
->clk
), "failed to get clock\n");
274 pdata
->sec_ao_map
= syscon_regmap_lookup_by_phandle
275 (pdev
->dev
.of_node
, "amlogic,ao-secure");
276 if (IS_ERR(pdata
->sec_ao_map
)) {
277 dev_err(dev
, "syscon regmap lookup failed.\n");
278 return PTR_ERR(pdata
->sec_ao_map
);
281 pdata
->tzd
= devm_thermal_of_zone_register(&pdev
->dev
,
284 &amlogic_thermal_ops
);
285 if (IS_ERR(pdata
->tzd
)) {
286 ret
= PTR_ERR(pdata
->tzd
);
287 dev_err(dev
, "Failed to register tsensor: %d\n", ret
);
291 devm_thermal_add_hwmon_sysfs(&pdev
->dev
, pdata
->tzd
);
293 ret
= amlogic_thermal_initialize(pdata
);
297 ret
= amlogic_thermal_enable(pdata
);
302 static void amlogic_thermal_remove(struct platform_device
*pdev
)
304 struct amlogic_thermal
*data
= platform_get_drvdata(pdev
);
306 amlogic_thermal_disable(data
);
309 static int amlogic_thermal_suspend(struct device
*dev
)
311 struct amlogic_thermal
*data
= dev_get_drvdata(dev
);
313 amlogic_thermal_disable(data
);
318 static int amlogic_thermal_resume(struct device
*dev
)
320 struct amlogic_thermal
*data
= dev_get_drvdata(dev
);
322 return amlogic_thermal_enable(data
);
325 static DEFINE_SIMPLE_DEV_PM_OPS(amlogic_thermal_pm_ops
,
326 amlogic_thermal_suspend
,
327 amlogic_thermal_resume
);
329 static struct platform_driver amlogic_thermal_driver
= {
331 .name
= "amlogic_thermal",
332 .pm
= pm_ptr(&amlogic_thermal_pm_ops
),
333 .of_match_table
= of_amlogic_thermal_match
,
335 .probe
= amlogic_thermal_probe
,
336 .remove
= amlogic_thermal_remove
,
339 module_platform_driver(amlogic_thermal_driver
);
341 MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
342 MODULE_DESCRIPTION("Amlogic thermal driver");
343 MODULE_LICENSE("GPL v2");