1421eda10SGuillaume La Roque // SPDX-License-Identifier: GPL-2.0+ 2421eda10SGuillaume La Roque /* 3421eda10SGuillaume La Roque * Amlogic Thermal Sensor Driver 4421eda10SGuillaume La Roque * 5421eda10SGuillaume La Roque * Copyright (C) 2017 Huan Biao <huan.biao@amlogic.com> 6421eda10SGuillaume La Roque * Copyright (C) 2019 Guillaume La Roque <glaroque@baylibre.com> 7421eda10SGuillaume La Roque * 8421eda10SGuillaume La Roque * Register value to celsius temperature formulas: 9421eda10SGuillaume La Roque * Read_Val m * U 10421eda10SGuillaume La Roque * U = ---------, Uptat = --------- 11421eda10SGuillaume La Roque * 2^16 1 + n * U 12421eda10SGuillaume La Roque * 13421eda10SGuillaume La Roque * Temperature = A * ( Uptat + u_efuse / 2^16 )- B 14421eda10SGuillaume La Roque * 15421eda10SGuillaume La Roque * A B m n : calibration parameters 16421eda10SGuillaume La Roque * u_efuse : fused calibration value, it's a signed 16 bits value 17421eda10SGuillaume La Roque */ 18421eda10SGuillaume La Roque 19421eda10SGuillaume La Roque #include <linux/bitfield.h> 20421eda10SGuillaume La Roque #include <linux/clk.h> 21421eda10SGuillaume La Roque #include <linux/io.h> 22421eda10SGuillaume La Roque #include <linux/mfd/syscon.h> 23421eda10SGuillaume La Roque #include <linux/module.h> 24421eda10SGuillaume La Roque #include <linux/of.h> 25421eda10SGuillaume La Roque #include <linux/of_address.h> 26421eda10SGuillaume La Roque #include <linux/of_device.h> 27421eda10SGuillaume La Roque #include <linux/platform_device.h> 28421eda10SGuillaume La Roque #include <linux/regmap.h> 29421eda10SGuillaume La Roque #include <linux/thermal.h> 30421eda10SGuillaume La Roque 31cb68a858SMartin Blumenstingl #include "thermal_hwmon.h" 32421eda10SGuillaume La Roque 33421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1 0x4 34421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_RSET_VBG BIT(12) 35421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_RSET_ADC BIT(11) 36421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_VCM_EN BIT(10) 37421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_VBG_EN BIT(9) 38421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_OUT_CTL BIT(6) 39421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_FILTER_EN BIT(5) 40421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_DEM_EN BIT(3) 41421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_CH_SEL GENMASK(1, 0) 42421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_ENABLE \ 43421eda10SGuillaume La Roque (TSENSOR_CFG_REG1_FILTER_EN | \ 44421eda10SGuillaume La Roque TSENSOR_CFG_REG1_VCM_EN | \ 45421eda10SGuillaume La Roque TSENSOR_CFG_REG1_VBG_EN | \ 46421eda10SGuillaume La Roque TSENSOR_CFG_REG1_DEM_EN | \ 47421eda10SGuillaume La Roque TSENSOR_CFG_REG1_CH_SEL) 48421eda10SGuillaume La Roque 49421eda10SGuillaume La Roque #define TSENSOR_STAT0 0x40 50421eda10SGuillaume La Roque 51421eda10SGuillaume La Roque #define TSENSOR_STAT9 0x64 52421eda10SGuillaume La Roque 53421eda10SGuillaume La Roque #define TSENSOR_READ_TEMP_MASK GENMASK(15, 0) 54421eda10SGuillaume La Roque #define TSENSOR_TEMP_MASK GENMASK(11, 0) 55421eda10SGuillaume La Roque 56421eda10SGuillaume La Roque #define TSENSOR_TRIM_SIGN_MASK BIT(15) 57421eda10SGuillaume La Roque #define TSENSOR_TRIM_TEMP_MASK GENMASK(14, 0) 58421eda10SGuillaume La Roque #define TSENSOR_TRIM_VERSION_MASK GENMASK(31, 24) 59421eda10SGuillaume La Roque 60421eda10SGuillaume La Roque #define TSENSOR_TRIM_VERSION(_version) \ 61421eda10SGuillaume La Roque FIELD_GET(TSENSOR_TRIM_VERSION_MASK, _version) 62421eda10SGuillaume La Roque 63421eda10SGuillaume La Roque #define TSENSOR_TRIM_CALIB_VALID_MASK (GENMASK(3, 2) | BIT(7)) 64421eda10SGuillaume La Roque 65421eda10SGuillaume La Roque #define TSENSOR_CALIB_OFFSET 1 66421eda10SGuillaume La Roque #define TSENSOR_CALIB_SHIFT 4 67421eda10SGuillaume La Roque 68421eda10SGuillaume La Roque /** 69421eda10SGuillaume La Roque * struct amlogic_thermal_soc_calib_data 70be7b848bSAmit Kucheria * @A: calibration parameters 71be7b848bSAmit Kucheria * @B: calibration parameters 72be7b848bSAmit Kucheria * @m: calibration parameters 73be7b848bSAmit Kucheria * @n: calibration parameters 74be7b848bSAmit Kucheria * 75421eda10SGuillaume La Roque * This structure is required for configuration of amlogic thermal driver. 76421eda10SGuillaume La Roque */ 77421eda10SGuillaume La Roque struct amlogic_thermal_soc_calib_data { 78421eda10SGuillaume La Roque int A; 79421eda10SGuillaume La Roque int B; 80421eda10SGuillaume La Roque int m; 81421eda10SGuillaume La Roque int n; 82421eda10SGuillaume La Roque }; 83421eda10SGuillaume La Roque 84421eda10SGuillaume La Roque /** 85421eda10SGuillaume La Roque * struct amlogic_thermal_data 86421eda10SGuillaume La Roque * @u_efuse_off: register offset to read fused calibration value 87421eda10SGuillaume La Roque * @calibration_parameters: calibration parameters structure pointer 88421eda10SGuillaume La Roque * @regmap_config: regmap config for the device 89421eda10SGuillaume La Roque * This structure is required for configuration of amlogic thermal driver. 90421eda10SGuillaume La Roque */ 91421eda10SGuillaume La Roque struct amlogic_thermal_data { 92421eda10SGuillaume La Roque int u_efuse_off; 93421eda10SGuillaume La Roque const struct amlogic_thermal_soc_calib_data *calibration_parameters; 94421eda10SGuillaume La Roque const struct regmap_config *regmap_config; 95421eda10SGuillaume La Roque }; 96421eda10SGuillaume La Roque 97421eda10SGuillaume La Roque struct amlogic_thermal { 98421eda10SGuillaume La Roque struct platform_device *pdev; 99421eda10SGuillaume La Roque const struct amlogic_thermal_data *data; 100421eda10SGuillaume La Roque struct regmap *regmap; 101421eda10SGuillaume La Roque struct regmap *sec_ao_map; 102421eda10SGuillaume La Roque struct clk *clk; 103421eda10SGuillaume La Roque struct thermal_zone_device *tzd; 104421eda10SGuillaume La Roque u32 trim_info; 105421eda10SGuillaume La Roque }; 106421eda10SGuillaume La Roque 107421eda10SGuillaume La Roque /* 108421eda10SGuillaume La Roque * Calculate a temperature value from a temperature code. 109421eda10SGuillaume La Roque * The unit of the temperature is degree milliCelsius. 110421eda10SGuillaume La Roque */ 111421eda10SGuillaume La Roque static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal *pdata, 112421eda10SGuillaume La Roque int temp_code) 113421eda10SGuillaume La Roque { 114421eda10SGuillaume La Roque const struct amlogic_thermal_soc_calib_data *param = 115421eda10SGuillaume La Roque pdata->data->calibration_parameters; 116421eda10SGuillaume La Roque int temp; 117421eda10SGuillaume La Roque s64 factor, Uptat, uefuse; 118421eda10SGuillaume La Roque 119421eda10SGuillaume La Roque uefuse = pdata->trim_info & TSENSOR_TRIM_SIGN_MASK ? 120421eda10SGuillaume La Roque ~(pdata->trim_info & TSENSOR_TRIM_TEMP_MASK) + 1 : 121421eda10SGuillaume La Roque (pdata->trim_info & TSENSOR_TRIM_TEMP_MASK); 122421eda10SGuillaume La Roque 123421eda10SGuillaume La Roque factor = param->n * temp_code; 124421eda10SGuillaume La Roque factor = div_s64(factor, 100); 125421eda10SGuillaume La Roque 126421eda10SGuillaume La Roque Uptat = temp_code * param->m; 127421eda10SGuillaume La Roque Uptat = div_s64(Uptat, 100); 128421eda10SGuillaume La Roque Uptat = Uptat * BIT(16); 129421eda10SGuillaume La Roque Uptat = div_s64(Uptat, BIT(16) + factor); 130421eda10SGuillaume La Roque 131421eda10SGuillaume La Roque temp = (Uptat + uefuse) * param->A; 132421eda10SGuillaume La Roque temp = div_s64(temp, BIT(16)); 133421eda10SGuillaume La Roque temp = (temp - param->B) * 100; 134421eda10SGuillaume La Roque 135421eda10SGuillaume La Roque return temp; 136421eda10SGuillaume La Roque } 137421eda10SGuillaume La Roque 138421eda10SGuillaume La Roque static int amlogic_thermal_initialize(struct amlogic_thermal *pdata) 139421eda10SGuillaume La Roque { 140421eda10SGuillaume La Roque int ret = 0; 141421eda10SGuillaume La Roque int ver; 142421eda10SGuillaume La Roque 143421eda10SGuillaume La Roque regmap_read(pdata->sec_ao_map, pdata->data->u_efuse_off, 144421eda10SGuillaume La Roque &pdata->trim_info); 145421eda10SGuillaume La Roque 146421eda10SGuillaume La Roque ver = TSENSOR_TRIM_VERSION(pdata->trim_info); 147421eda10SGuillaume La Roque 148421eda10SGuillaume La Roque if ((ver & TSENSOR_TRIM_CALIB_VALID_MASK) == 0) { 149421eda10SGuillaume La Roque ret = -EINVAL; 150421eda10SGuillaume La Roque dev_err(&pdata->pdev->dev, 151421eda10SGuillaume La Roque "tsensor thermal calibration not supported: 0x%x!\n", 152421eda10SGuillaume La Roque ver); 153421eda10SGuillaume La Roque } 154421eda10SGuillaume La Roque 155421eda10SGuillaume La Roque return ret; 156421eda10SGuillaume La Roque } 157421eda10SGuillaume La Roque 158421eda10SGuillaume La Roque static int amlogic_thermal_enable(struct amlogic_thermal *data) 159421eda10SGuillaume La Roque { 160421eda10SGuillaume La Roque int ret; 161421eda10SGuillaume La Roque 162421eda10SGuillaume La Roque ret = clk_prepare_enable(data->clk); 163421eda10SGuillaume La Roque if (ret) 164421eda10SGuillaume La Roque return ret; 165421eda10SGuillaume La Roque 166421eda10SGuillaume La Roque regmap_update_bits(data->regmap, TSENSOR_CFG_REG1, 167421eda10SGuillaume La Roque TSENSOR_CFG_REG1_ENABLE, TSENSOR_CFG_REG1_ENABLE); 168421eda10SGuillaume La Roque 169421eda10SGuillaume La Roque return 0; 170421eda10SGuillaume La Roque } 171421eda10SGuillaume La Roque 172421eda10SGuillaume La Roque static int amlogic_thermal_disable(struct amlogic_thermal *data) 173421eda10SGuillaume La Roque { 174421eda10SGuillaume La Roque regmap_update_bits(data->regmap, TSENSOR_CFG_REG1, 175421eda10SGuillaume La Roque TSENSOR_CFG_REG1_ENABLE, 0); 176421eda10SGuillaume La Roque clk_disable_unprepare(data->clk); 177421eda10SGuillaume La Roque 178421eda10SGuillaume La Roque return 0; 179421eda10SGuillaume La Roque } 180421eda10SGuillaume La Roque 1811240fd65SDaniel Lezcano static int amlogic_thermal_get_temp(struct thermal_zone_device *tz, int *temp) 182421eda10SGuillaume La Roque { 183421eda10SGuillaume La Roque unsigned int tval; 1845f68d078SDaniel Lezcano struct amlogic_thermal *pdata = thermal_zone_device_priv(tz); 185421eda10SGuillaume La Roque 1861240fd65SDaniel Lezcano if (!pdata) 187421eda10SGuillaume La Roque return -EINVAL; 188421eda10SGuillaume La Roque 189421eda10SGuillaume La Roque regmap_read(pdata->regmap, TSENSOR_STAT0, &tval); 190421eda10SGuillaume La Roque *temp = 191421eda10SGuillaume La Roque amlogic_thermal_code_to_millicelsius(pdata, 192421eda10SGuillaume La Roque tval & TSENSOR_READ_TEMP_MASK); 193421eda10SGuillaume La Roque 194421eda10SGuillaume La Roque return 0; 195421eda10SGuillaume La Roque } 196421eda10SGuillaume La Roque 1971240fd65SDaniel Lezcano static const struct thermal_zone_device_ops amlogic_thermal_ops = { 198421eda10SGuillaume La Roque .get_temp = amlogic_thermal_get_temp, 199421eda10SGuillaume La Roque }; 200421eda10SGuillaume La Roque 201421eda10SGuillaume La Roque static const struct regmap_config amlogic_thermal_regmap_config_g12a = { 202421eda10SGuillaume La Roque .reg_bits = 8, 203421eda10SGuillaume La Roque .val_bits = 32, 204421eda10SGuillaume La Roque .reg_stride = 4, 205421eda10SGuillaume La Roque .max_register = TSENSOR_STAT9, 206421eda10SGuillaume La Roque }; 207421eda10SGuillaume La Roque 208421eda10SGuillaume La Roque static const struct amlogic_thermal_soc_calib_data amlogic_thermal_g12a = { 209421eda10SGuillaume La Roque .A = 9411, 210421eda10SGuillaume La Roque .B = 3159, 211421eda10SGuillaume La Roque .m = 424, 212421eda10SGuillaume La Roque .n = 324, 213421eda10SGuillaume La Roque }; 214421eda10SGuillaume La Roque 215421eda10SGuillaume La Roque static const struct amlogic_thermal_data amlogic_thermal_g12a_cpu_param = { 216421eda10SGuillaume La Roque .u_efuse_off = 0x128, 217421eda10SGuillaume La Roque .calibration_parameters = &amlogic_thermal_g12a, 218421eda10SGuillaume La Roque .regmap_config = &amlogic_thermal_regmap_config_g12a, 219421eda10SGuillaume La Roque }; 220421eda10SGuillaume La Roque 221421eda10SGuillaume La Roque static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param = { 222421eda10SGuillaume La Roque .u_efuse_off = 0xf0, 223421eda10SGuillaume La Roque .calibration_parameters = &amlogic_thermal_g12a, 224421eda10SGuillaume La Roque .regmap_config = &amlogic_thermal_regmap_config_g12a, 225421eda10SGuillaume La Roque }; 226421eda10SGuillaume La Roque 227421eda10SGuillaume La Roque static const struct of_device_id of_amlogic_thermal_match[] = { 228421eda10SGuillaume La Roque { 229421eda10SGuillaume La Roque .compatible = "amlogic,g12a-ddr-thermal", 230421eda10SGuillaume La Roque .data = &amlogic_thermal_g12a_ddr_param, 231421eda10SGuillaume La Roque }, 232421eda10SGuillaume La Roque { 233421eda10SGuillaume La Roque .compatible = "amlogic,g12a-cpu-thermal", 234421eda10SGuillaume La Roque .data = &amlogic_thermal_g12a_cpu_param, 235421eda10SGuillaume La Roque }, 236421eda10SGuillaume La Roque { /* sentinel */ } 237421eda10SGuillaume La Roque }; 238421eda10SGuillaume La Roque MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match); 239421eda10SGuillaume La Roque 240421eda10SGuillaume La Roque static int amlogic_thermal_probe(struct platform_device *pdev) 241421eda10SGuillaume La Roque { 242421eda10SGuillaume La Roque struct amlogic_thermal *pdata; 243421eda10SGuillaume La Roque struct device *dev = &pdev->dev; 244421eda10SGuillaume La Roque void __iomem *base; 245421eda10SGuillaume La Roque int ret; 246421eda10SGuillaume La Roque 247421eda10SGuillaume La Roque pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 248421eda10SGuillaume La Roque if (!pdata) 249421eda10SGuillaume La Roque return -ENOMEM; 250421eda10SGuillaume La Roque 251421eda10SGuillaume La Roque pdata->data = of_device_get_match_data(dev); 252421eda10SGuillaume La Roque pdata->pdev = pdev; 253421eda10SGuillaume La Roque platform_set_drvdata(pdev, pdata); 254421eda10SGuillaume La Roque 255421eda10SGuillaume La Roque base = devm_platform_ioremap_resource(pdev, 0); 256e042e95bSTang Bin if (IS_ERR(base)) 257421eda10SGuillaume La Roque return PTR_ERR(base); 258421eda10SGuillaume La Roque 259421eda10SGuillaume La Roque pdata->regmap = devm_regmap_init_mmio(dev, base, 260421eda10SGuillaume La Roque pdata->data->regmap_config); 261421eda10SGuillaume La Roque if (IS_ERR(pdata->regmap)) 262421eda10SGuillaume La Roque return PTR_ERR(pdata->regmap); 263421eda10SGuillaume La Roque 264421eda10SGuillaume La Roque pdata->clk = devm_clk_get(dev, NULL); 265*46d6cbb8SYe Xingchen if (IS_ERR(pdata->clk)) 266*46d6cbb8SYe Xingchen return dev_err_probe(dev, PTR_ERR(pdata->clk), "failed to get clock\n"); 267421eda10SGuillaume La Roque 268421eda10SGuillaume La Roque pdata->sec_ao_map = syscon_regmap_lookup_by_phandle 269421eda10SGuillaume La Roque (pdev->dev.of_node, "amlogic,ao-secure"); 270421eda10SGuillaume La Roque if (IS_ERR(pdata->sec_ao_map)) { 271421eda10SGuillaume La Roque dev_err(dev, "syscon regmap lookup failed.\n"); 272421eda10SGuillaume La Roque return PTR_ERR(pdata->sec_ao_map); 273421eda10SGuillaume La Roque } 274421eda10SGuillaume La Roque 2751240fd65SDaniel Lezcano pdata->tzd = devm_thermal_of_zone_register(&pdev->dev, 276421eda10SGuillaume La Roque 0, 277421eda10SGuillaume La Roque pdata, 278421eda10SGuillaume La Roque &amlogic_thermal_ops); 279421eda10SGuillaume La Roque if (IS_ERR(pdata->tzd)) { 280421eda10SGuillaume La Roque ret = PTR_ERR(pdata->tzd); 281421eda10SGuillaume La Roque dev_err(dev, "Failed to register tsensor: %d\n", ret); 282421eda10SGuillaume La Roque return ret; 283421eda10SGuillaume La Roque } 284421eda10SGuillaume La Roque 2854a16c190SDaniel Lezcano if (devm_thermal_add_hwmon_sysfs(&pdev->dev, pdata->tzd)) 286cb68a858SMartin Blumenstingl dev_warn(&pdev->dev, "Failed to add hwmon sysfs attributes\n"); 287cb68a858SMartin Blumenstingl 288421eda10SGuillaume La Roque ret = amlogic_thermal_initialize(pdata); 289421eda10SGuillaume La Roque if (ret) 290421eda10SGuillaume La Roque return ret; 291421eda10SGuillaume La Roque 292421eda10SGuillaume La Roque ret = amlogic_thermal_enable(pdata); 293421eda10SGuillaume La Roque 294421eda10SGuillaume La Roque return ret; 295421eda10SGuillaume La Roque } 296421eda10SGuillaume La Roque 297421eda10SGuillaume La Roque static int amlogic_thermal_remove(struct platform_device *pdev) 298421eda10SGuillaume La Roque { 299421eda10SGuillaume La Roque struct amlogic_thermal *data = platform_get_drvdata(pdev); 300421eda10SGuillaume La Roque 301421eda10SGuillaume La Roque return amlogic_thermal_disable(data); 302421eda10SGuillaume La Roque } 303421eda10SGuillaume La Roque 304421eda10SGuillaume La Roque static int __maybe_unused amlogic_thermal_suspend(struct device *dev) 305421eda10SGuillaume La Roque { 306421eda10SGuillaume La Roque struct amlogic_thermal *data = dev_get_drvdata(dev); 307421eda10SGuillaume La Roque 308421eda10SGuillaume La Roque return amlogic_thermal_disable(data); 309421eda10SGuillaume La Roque } 310421eda10SGuillaume La Roque 311421eda10SGuillaume La Roque static int __maybe_unused amlogic_thermal_resume(struct device *dev) 312421eda10SGuillaume La Roque { 313421eda10SGuillaume La Roque struct amlogic_thermal *data = dev_get_drvdata(dev); 314421eda10SGuillaume La Roque 315421eda10SGuillaume La Roque return amlogic_thermal_enable(data); 316421eda10SGuillaume La Roque } 317421eda10SGuillaume La Roque 318421eda10SGuillaume La Roque static SIMPLE_DEV_PM_OPS(amlogic_thermal_pm_ops, 319421eda10SGuillaume La Roque amlogic_thermal_suspend, amlogic_thermal_resume); 320421eda10SGuillaume La Roque 321421eda10SGuillaume La Roque static struct platform_driver amlogic_thermal_driver = { 322421eda10SGuillaume La Roque .driver = { 323421eda10SGuillaume La Roque .name = "amlogic_thermal", 324421eda10SGuillaume La Roque .pm = &amlogic_thermal_pm_ops, 325421eda10SGuillaume La Roque .of_match_table = of_amlogic_thermal_match, 326421eda10SGuillaume La Roque }, 327421eda10SGuillaume La Roque .probe = amlogic_thermal_probe, 328421eda10SGuillaume La Roque .remove = amlogic_thermal_remove, 329421eda10SGuillaume La Roque }; 330421eda10SGuillaume La Roque 331421eda10SGuillaume La Roque module_platform_driver(amlogic_thermal_driver); 332421eda10SGuillaume La Roque 333421eda10SGuillaume La Roque MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>"); 334421eda10SGuillaume La Roque MODULE_DESCRIPTION("Amlogic thermal driver"); 335421eda10SGuillaume La Roque MODULE_LICENSE("GPL v2"); 336