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/of_address.h>
26 #include <linux/of_device.h>
27 #include <linux/platform_device.h>
28 #include <linux/regmap.h>
29 #include <linux/thermal.h>
31 #include "thermal_core.h"
33 #define TSENSOR_CFG_REG1 0x4
34 #define TSENSOR_CFG_REG1_RSET_VBG BIT(12)
35 #define TSENSOR_CFG_REG1_RSET_ADC BIT(11)
36 #define TSENSOR_CFG_REG1_VCM_EN BIT(10)
37 #define TSENSOR_CFG_REG1_VBG_EN BIT(9)
38 #define TSENSOR_CFG_REG1_OUT_CTL BIT(6)
39 #define TSENSOR_CFG_REG1_FILTER_EN BIT(5)
40 #define TSENSOR_CFG_REG1_DEM_EN BIT(3)
41 #define TSENSOR_CFG_REG1_CH_SEL GENMASK(1, 0)
42 #define TSENSOR_CFG_REG1_ENABLE \
43 (TSENSOR_CFG_REG1_FILTER_EN | \
44 TSENSOR_CFG_REG1_VCM_EN | \
45 TSENSOR_CFG_REG1_VBG_EN | \
46 TSENSOR_CFG_REG1_DEM_EN | \
47 TSENSOR_CFG_REG1_CH_SEL)
49 #define TSENSOR_STAT0 0x40
51 #define TSENSOR_STAT9 0x64
53 #define TSENSOR_READ_TEMP_MASK GENMASK(15, 0)
54 #define TSENSOR_TEMP_MASK GENMASK(11, 0)
56 #define TSENSOR_TRIM_SIGN_MASK BIT(15)
57 #define TSENSOR_TRIM_TEMP_MASK GENMASK(14, 0)
58 #define TSENSOR_TRIM_VERSION_MASK GENMASK(31, 24)
60 #define TSENSOR_TRIM_VERSION(_version) \
61 FIELD_GET(TSENSOR_TRIM_VERSION_MASK, _version)
63 #define TSENSOR_TRIM_CALIB_VALID_MASK (GENMASK(3, 2) | BIT(7))
65 #define TSENSOR_CALIB_OFFSET 1
66 #define TSENSOR_CALIB_SHIFT 4
69 * struct amlogic_thermal_soc_calib_data
70 * @A: calibration parameters
71 * @B: calibration parameters
72 * @m: calibration parameters
73 * @n: calibration parameters
75 * This structure is required for configuration of amlogic thermal driver.
77 struct amlogic_thermal_soc_calib_data
{
85 * struct amlogic_thermal_data
86 * @u_efuse_off: register offset to read fused calibration value
87 * @calibration_parameters: calibration parameters structure pointer
88 * @regmap_config: regmap config for the device
89 * This structure is required for configuration of amlogic thermal driver.
91 struct amlogic_thermal_data
{
93 const struct amlogic_thermal_soc_calib_data
*calibration_parameters
;
94 const struct regmap_config
*regmap_config
;
97 struct amlogic_thermal
{
98 struct platform_device
*pdev
;
99 const struct amlogic_thermal_data
*data
;
100 struct regmap
*regmap
;
101 struct regmap
*sec_ao_map
;
103 struct thermal_zone_device
*tzd
;
108 * Calculate a temperature value from a temperature code.
109 * The unit of the temperature is degree milliCelsius.
111 static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal
*pdata
,
114 const struct amlogic_thermal_soc_calib_data
*param
=
115 pdata
->data
->calibration_parameters
;
117 s64 factor
, Uptat
, uefuse
;
119 uefuse
= pdata
->trim_info
& TSENSOR_TRIM_SIGN_MASK
?
120 ~(pdata
->trim_info
& TSENSOR_TRIM_TEMP_MASK
) + 1 :
121 (pdata
->trim_info
& TSENSOR_TRIM_TEMP_MASK
);
123 factor
= param
->n
* temp_code
;
124 factor
= div_s64(factor
, 100);
126 Uptat
= temp_code
* param
->m
;
127 Uptat
= div_s64(Uptat
, 100);
128 Uptat
= Uptat
* BIT(16);
129 Uptat
= div_s64(Uptat
, BIT(16) + factor
);
131 temp
= (Uptat
+ uefuse
) * param
->A
;
132 temp
= div_s64(temp
, BIT(16));
133 temp
= (temp
- param
->B
) * 100;
138 static int amlogic_thermal_initialize(struct amlogic_thermal
*pdata
)
143 regmap_read(pdata
->sec_ao_map
, pdata
->data
->u_efuse_off
,
146 ver
= TSENSOR_TRIM_VERSION(pdata
->trim_info
);
148 if ((ver
& TSENSOR_TRIM_CALIB_VALID_MASK
) == 0) {
150 dev_err(&pdata
->pdev
->dev
,
151 "tsensor thermal calibration not supported: 0x%x!\n",
158 static int amlogic_thermal_enable(struct amlogic_thermal
*data
)
162 ret
= clk_prepare_enable(data
->clk
);
166 regmap_update_bits(data
->regmap
, TSENSOR_CFG_REG1
,
167 TSENSOR_CFG_REG1_ENABLE
, TSENSOR_CFG_REG1_ENABLE
);
172 static int amlogic_thermal_disable(struct amlogic_thermal
*data
)
174 regmap_update_bits(data
->regmap
, TSENSOR_CFG_REG1
,
175 TSENSOR_CFG_REG1_ENABLE
, 0);
176 clk_disable_unprepare(data
->clk
);
181 static int amlogic_thermal_get_temp(void *data
, int *temp
)
184 struct amlogic_thermal
*pdata
= data
;
189 regmap_read(pdata
->regmap
, TSENSOR_STAT0
, &tval
);
191 amlogic_thermal_code_to_millicelsius(pdata
,
192 tval
& TSENSOR_READ_TEMP_MASK
);
197 static const struct thermal_zone_of_device_ops amlogic_thermal_ops
= {
198 .get_temp
= amlogic_thermal_get_temp
,
201 static const struct regmap_config amlogic_thermal_regmap_config_g12a
= {
205 .max_register
= TSENSOR_STAT9
,
208 static const struct amlogic_thermal_soc_calib_data amlogic_thermal_g12a
= {
215 static const struct amlogic_thermal_data amlogic_thermal_g12a_cpu_param
= {
216 .u_efuse_off
= 0x128,
217 .calibration_parameters
= &amlogic_thermal_g12a
,
218 .regmap_config
= &amlogic_thermal_regmap_config_g12a
,
221 static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param
= {
223 .calibration_parameters
= &amlogic_thermal_g12a
,
224 .regmap_config
= &amlogic_thermal_regmap_config_g12a
,
227 static const struct of_device_id of_amlogic_thermal_match
[] = {
229 .compatible
= "amlogic,g12a-ddr-thermal",
230 .data
= &amlogic_thermal_g12a_ddr_param
,
233 .compatible
= "amlogic,g12a-cpu-thermal",
234 .data
= &amlogic_thermal_g12a_cpu_param
,
238 MODULE_DEVICE_TABLE(of
, of_amlogic_thermal_match
);
240 static int amlogic_thermal_probe(struct platform_device
*pdev
)
242 struct amlogic_thermal
*pdata
;
243 struct device
*dev
= &pdev
->dev
;
247 pdata
= devm_kzalloc(dev
, sizeof(*pdata
), GFP_KERNEL
);
251 pdata
->data
= of_device_get_match_data(dev
);
253 platform_set_drvdata(pdev
, pdata
);
255 base
= devm_platform_ioremap_resource(pdev
, 0);
257 dev_err(dev
, "failed to get io address\n");
258 return PTR_ERR(base
);
261 pdata
->regmap
= devm_regmap_init_mmio(dev
, base
,
262 pdata
->data
->regmap_config
);
263 if (IS_ERR(pdata
->regmap
))
264 return PTR_ERR(pdata
->regmap
);
266 pdata
->clk
= devm_clk_get(dev
, NULL
);
267 if (IS_ERR(pdata
->clk
)) {
268 if (PTR_ERR(pdata
->clk
) != -EPROBE_DEFER
)
269 dev_err(dev
, "failed to get clock\n");
270 return PTR_ERR(pdata
->clk
);
273 pdata
->sec_ao_map
= syscon_regmap_lookup_by_phandle
274 (pdev
->dev
.of_node
, "amlogic,ao-secure");
275 if (IS_ERR(pdata
->sec_ao_map
)) {
276 dev_err(dev
, "syscon regmap lookup failed.\n");
277 return PTR_ERR(pdata
->sec_ao_map
);
280 pdata
->tzd
= devm_thermal_zone_of_sensor_register(&pdev
->dev
,
283 &amlogic_thermal_ops
);
284 if (IS_ERR(pdata
->tzd
)) {
285 ret
= PTR_ERR(pdata
->tzd
);
286 dev_err(dev
, "Failed to register tsensor: %d\n", ret
);
290 ret
= amlogic_thermal_initialize(pdata
);
294 ret
= amlogic_thermal_enable(pdata
);
299 static int amlogic_thermal_remove(struct platform_device
*pdev
)
301 struct amlogic_thermal
*data
= platform_get_drvdata(pdev
);
303 return amlogic_thermal_disable(data
);
306 static int __maybe_unused
amlogic_thermal_suspend(struct device
*dev
)
308 struct amlogic_thermal
*data
= dev_get_drvdata(dev
);
310 return amlogic_thermal_disable(data
);
313 static int __maybe_unused
amlogic_thermal_resume(struct device
*dev
)
315 struct amlogic_thermal
*data
= dev_get_drvdata(dev
);
317 return amlogic_thermal_enable(data
);
320 static SIMPLE_DEV_PM_OPS(amlogic_thermal_pm_ops
,
321 amlogic_thermal_suspend
, amlogic_thermal_resume
);
323 static struct platform_driver amlogic_thermal_driver
= {
325 .name
= "amlogic_thermal",
326 .pm
= &amlogic_thermal_pm_ops
,
327 .of_match_table
= of_amlogic_thermal_match
,
329 .probe
= amlogic_thermal_probe
,
330 .remove
= amlogic_thermal_remove
,
333 module_platform_driver(amlogic_thermal_driver
);
335 MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
336 MODULE_DESCRIPTION("Amlogic thermal driver");
337 MODULE_LICENSE("GPL v2");