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