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