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"
32 #include "thermal_hwmon.h"
34 #define TSENSOR_CFG_REG1 0x4
35 #define TSENSOR_CFG_REG1_RSET_VBG BIT(12)
36 #define TSENSOR_CFG_REG1_RSET_ADC BIT(11)
37 #define TSENSOR_CFG_REG1_VCM_EN BIT(10)
38 #define TSENSOR_CFG_REG1_VBG_EN BIT(9)
39 #define TSENSOR_CFG_REG1_OUT_CTL BIT(6)
40 #define TSENSOR_CFG_REG1_FILTER_EN BIT(5)
41 #define TSENSOR_CFG_REG1_DEM_EN BIT(3)
42 #define TSENSOR_CFG_REG1_CH_SEL GENMASK(1, 0)
43 #define TSENSOR_CFG_REG1_ENABLE \
44 (TSENSOR_CFG_REG1_FILTER_EN | \
45 TSENSOR_CFG_REG1_VCM_EN | \
46 TSENSOR_CFG_REG1_VBG_EN | \
47 TSENSOR_CFG_REG1_DEM_EN | \
48 TSENSOR_CFG_REG1_CH_SEL)
50 #define TSENSOR_STAT0 0x40
52 #define TSENSOR_STAT9 0x64
54 #define TSENSOR_READ_TEMP_MASK GENMASK(15, 0)
55 #define TSENSOR_TEMP_MASK GENMASK(11, 0)
57 #define TSENSOR_TRIM_SIGN_MASK BIT(15)
58 #define TSENSOR_TRIM_TEMP_MASK GENMASK(14, 0)
59 #define TSENSOR_TRIM_VERSION_MASK GENMASK(31, 24)
61 #define TSENSOR_TRIM_VERSION(_version) \
62 FIELD_GET(TSENSOR_TRIM_VERSION_MASK, _version)
64 #define TSENSOR_TRIM_CALIB_VALID_MASK (GENMASK(3, 2) | BIT(7))
66 #define TSENSOR_CALIB_OFFSET 1
67 #define TSENSOR_CALIB_SHIFT 4
70 * struct amlogic_thermal_soc_calib_data
71 * @A: calibration parameters
72 * @B: calibration parameters
73 * @m: calibration parameters
74 * @n: calibration parameters
76 * This structure is required for configuration of amlogic thermal driver.
78 struct amlogic_thermal_soc_calib_data
{
86 * struct amlogic_thermal_data
87 * @u_efuse_off: register offset to read fused calibration value
88 * @calibration_parameters: calibration parameters structure pointer
89 * @regmap_config: regmap config for the device
90 * This structure is required for configuration of amlogic thermal driver.
92 struct amlogic_thermal_data
{
94 const struct amlogic_thermal_soc_calib_data
*calibration_parameters
;
95 const struct regmap_config
*regmap_config
;
98 struct amlogic_thermal
{
99 struct platform_device
*pdev
;
100 const struct amlogic_thermal_data
*data
;
101 struct regmap
*regmap
;
102 struct regmap
*sec_ao_map
;
104 struct thermal_zone_device
*tzd
;
109 * Calculate a temperature value from a temperature code.
110 * The unit of the temperature is degree milliCelsius.
112 static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal
*pdata
,
115 const struct amlogic_thermal_soc_calib_data
*param
=
116 pdata
->data
->calibration_parameters
;
118 s64 factor
, Uptat
, uefuse
;
120 uefuse
= pdata
->trim_info
& TSENSOR_TRIM_SIGN_MASK
?
121 ~(pdata
->trim_info
& TSENSOR_TRIM_TEMP_MASK
) + 1 :
122 (pdata
->trim_info
& TSENSOR_TRIM_TEMP_MASK
);
124 factor
= param
->n
* temp_code
;
125 factor
= div_s64(factor
, 100);
127 Uptat
= temp_code
* param
->m
;
128 Uptat
= div_s64(Uptat
, 100);
129 Uptat
= Uptat
* BIT(16);
130 Uptat
= div_s64(Uptat
, BIT(16) + factor
);
132 temp
= (Uptat
+ uefuse
) * param
->A
;
133 temp
= div_s64(temp
, BIT(16));
134 temp
= (temp
- param
->B
) * 100;
139 static int amlogic_thermal_initialize(struct amlogic_thermal
*pdata
)
144 regmap_read(pdata
->sec_ao_map
, pdata
->data
->u_efuse_off
,
147 ver
= TSENSOR_TRIM_VERSION(pdata
->trim_info
);
149 if ((ver
& TSENSOR_TRIM_CALIB_VALID_MASK
) == 0) {
151 dev_err(&pdata
->pdev
->dev
,
152 "tsensor thermal calibration not supported: 0x%x!\n",
159 static int amlogic_thermal_enable(struct amlogic_thermal
*data
)
163 ret
= clk_prepare_enable(data
->clk
);
167 regmap_update_bits(data
->regmap
, TSENSOR_CFG_REG1
,
168 TSENSOR_CFG_REG1_ENABLE
, TSENSOR_CFG_REG1_ENABLE
);
173 static int amlogic_thermal_disable(struct amlogic_thermal
*data
)
175 regmap_update_bits(data
->regmap
, TSENSOR_CFG_REG1
,
176 TSENSOR_CFG_REG1_ENABLE
, 0);
177 clk_disable_unprepare(data
->clk
);
182 static int amlogic_thermal_get_temp(void *data
, int *temp
)
185 struct amlogic_thermal
*pdata
= data
;
190 regmap_read(pdata
->regmap
, TSENSOR_STAT0
, &tval
);
192 amlogic_thermal_code_to_millicelsius(pdata
,
193 tval
& TSENSOR_READ_TEMP_MASK
);
198 static const struct thermal_zone_of_device_ops amlogic_thermal_ops
= {
199 .get_temp
= amlogic_thermal_get_temp
,
202 static const struct regmap_config amlogic_thermal_regmap_config_g12a
= {
206 .max_register
= TSENSOR_STAT9
,
209 static const struct amlogic_thermal_soc_calib_data amlogic_thermal_g12a
= {
216 static const struct amlogic_thermal_data amlogic_thermal_g12a_cpu_param
= {
217 .u_efuse_off
= 0x128,
218 .calibration_parameters
= &amlogic_thermal_g12a
,
219 .regmap_config
= &amlogic_thermal_regmap_config_g12a
,
222 static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param
= {
224 .calibration_parameters
= &amlogic_thermal_g12a
,
225 .regmap_config
= &amlogic_thermal_regmap_config_g12a
,
228 static const struct of_device_id of_amlogic_thermal_match
[] = {
230 .compatible
= "amlogic,g12a-ddr-thermal",
231 .data
= &amlogic_thermal_g12a_ddr_param
,
234 .compatible
= "amlogic,g12a-cpu-thermal",
235 .data
= &amlogic_thermal_g12a_cpu_param
,
239 MODULE_DEVICE_TABLE(of
, of_amlogic_thermal_match
);
241 static int amlogic_thermal_probe(struct platform_device
*pdev
)
243 struct amlogic_thermal
*pdata
;
244 struct device
*dev
= &pdev
->dev
;
248 pdata
= devm_kzalloc(dev
, sizeof(*pdata
), GFP_KERNEL
);
252 pdata
->data
= of_device_get_match_data(dev
);
254 platform_set_drvdata(pdev
, pdata
);
256 base
= devm_platform_ioremap_resource(pdev
, 0);
258 dev_err(dev
, "failed to get io address\n");
259 return PTR_ERR(base
);
262 pdata
->regmap
= devm_regmap_init_mmio(dev
, base
,
263 pdata
->data
->regmap_config
);
264 if (IS_ERR(pdata
->regmap
))
265 return PTR_ERR(pdata
->regmap
);
267 pdata
->clk
= devm_clk_get(dev
, NULL
);
268 if (IS_ERR(pdata
->clk
)) {
269 if (PTR_ERR(pdata
->clk
) != -EPROBE_DEFER
)
270 dev_err(dev
, "failed to get clock\n");
271 return PTR_ERR(pdata
->clk
);
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_zone_of_sensor_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 if (devm_thermal_add_hwmon_sysfs(pdata
->tzd
))
292 dev_warn(&pdev
->dev
, "Failed to add hwmon sysfs attributes\n");
294 ret
= amlogic_thermal_initialize(pdata
);
298 ret
= amlogic_thermal_enable(pdata
);
303 static int amlogic_thermal_remove(struct platform_device
*pdev
)
305 struct amlogic_thermal
*data
= platform_get_drvdata(pdev
);
307 return amlogic_thermal_disable(data
);
310 static int __maybe_unused
amlogic_thermal_suspend(struct device
*dev
)
312 struct amlogic_thermal
*data
= dev_get_drvdata(dev
);
314 return amlogic_thermal_disable(data
);
317 static int __maybe_unused
amlogic_thermal_resume(struct device
*dev
)
319 struct amlogic_thermal
*data
= dev_get_drvdata(dev
);
321 return amlogic_thermal_enable(data
);
324 static SIMPLE_DEV_PM_OPS(amlogic_thermal_pm_ops
,
325 amlogic_thermal_suspend
, amlogic_thermal_resume
);
327 static struct platform_driver amlogic_thermal_driver
= {
329 .name
= "amlogic_thermal",
330 .pm
= &amlogic_thermal_pm_ops
,
331 .of_match_table
= of_amlogic_thermal_match
,
333 .probe
= amlogic_thermal_probe
,
334 .remove
= amlogic_thermal_remove
,
337 module_platform_driver(amlogic_thermal_driver
);
339 MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
340 MODULE_DESCRIPTION("Amlogic thermal driver");
341 MODULE_LICENSE("GPL v2");