1*421eda10SGuillaume La Roque // SPDX-License-Identifier: GPL-2.0+ 2*421eda10SGuillaume La Roque /* 3*421eda10SGuillaume La Roque * Amlogic Thermal Sensor Driver 4*421eda10SGuillaume La Roque * 5*421eda10SGuillaume La Roque * Copyright (C) 2017 Huan Biao <huan.biao@amlogic.com> 6*421eda10SGuillaume La Roque * Copyright (C) 2019 Guillaume La Roque <glaroque@baylibre.com> 7*421eda10SGuillaume La Roque * 8*421eda10SGuillaume La Roque * Register value to celsius temperature formulas: 9*421eda10SGuillaume La Roque * Read_Val m * U 10*421eda10SGuillaume La Roque * U = ---------, Uptat = --------- 11*421eda10SGuillaume La Roque * 2^16 1 + n * U 12*421eda10SGuillaume La Roque * 13*421eda10SGuillaume La Roque * Temperature = A * ( Uptat + u_efuse / 2^16 )- B 14*421eda10SGuillaume La Roque * 15*421eda10SGuillaume La Roque * A B m n : calibration parameters 16*421eda10SGuillaume La Roque * u_efuse : fused calibration value, it's a signed 16 bits value 17*421eda10SGuillaume La Roque */ 18*421eda10SGuillaume La Roque 19*421eda10SGuillaume La Roque #include <linux/bitfield.h> 20*421eda10SGuillaume La Roque #include <linux/clk.h> 21*421eda10SGuillaume La Roque #include <linux/io.h> 22*421eda10SGuillaume La Roque #include <linux/mfd/syscon.h> 23*421eda10SGuillaume La Roque #include <linux/module.h> 24*421eda10SGuillaume La Roque #include <linux/of.h> 25*421eda10SGuillaume La Roque #include <linux/of_address.h> 26*421eda10SGuillaume La Roque #include <linux/of_device.h> 27*421eda10SGuillaume La Roque #include <linux/platform_device.h> 28*421eda10SGuillaume La Roque #include <linux/regmap.h> 29*421eda10SGuillaume La Roque #include <linux/thermal.h> 30*421eda10SGuillaume La Roque 31*421eda10SGuillaume La Roque #include "thermal_core.h" 32*421eda10SGuillaume La Roque 33*421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1 0x4 34*421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_RSET_VBG BIT(12) 35*421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_RSET_ADC BIT(11) 36*421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_VCM_EN BIT(10) 37*421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_VBG_EN BIT(9) 38*421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_OUT_CTL BIT(6) 39*421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_FILTER_EN BIT(5) 40*421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_DEM_EN BIT(3) 41*421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_CH_SEL GENMASK(1, 0) 42*421eda10SGuillaume La Roque #define TSENSOR_CFG_REG1_ENABLE \ 43*421eda10SGuillaume La Roque (TSENSOR_CFG_REG1_FILTER_EN | \ 44*421eda10SGuillaume La Roque TSENSOR_CFG_REG1_VCM_EN | \ 45*421eda10SGuillaume La Roque TSENSOR_CFG_REG1_VBG_EN | \ 46*421eda10SGuillaume La Roque TSENSOR_CFG_REG1_DEM_EN | \ 47*421eda10SGuillaume La Roque TSENSOR_CFG_REG1_CH_SEL) 48*421eda10SGuillaume La Roque 49*421eda10SGuillaume La Roque #define TSENSOR_STAT0 0x40 50*421eda10SGuillaume La Roque 51*421eda10SGuillaume La Roque #define TSENSOR_STAT9 0x64 52*421eda10SGuillaume La Roque 53*421eda10SGuillaume La Roque #define TSENSOR_READ_TEMP_MASK GENMASK(15, 0) 54*421eda10SGuillaume La Roque #define TSENSOR_TEMP_MASK GENMASK(11, 0) 55*421eda10SGuillaume La Roque 56*421eda10SGuillaume La Roque #define TSENSOR_TRIM_SIGN_MASK BIT(15) 57*421eda10SGuillaume La Roque #define TSENSOR_TRIM_TEMP_MASK GENMASK(14, 0) 58*421eda10SGuillaume La Roque #define TSENSOR_TRIM_VERSION_MASK GENMASK(31, 24) 59*421eda10SGuillaume La Roque 60*421eda10SGuillaume La Roque #define TSENSOR_TRIM_VERSION(_version) \ 61*421eda10SGuillaume La Roque FIELD_GET(TSENSOR_TRIM_VERSION_MASK, _version) 62*421eda10SGuillaume La Roque 63*421eda10SGuillaume La Roque #define TSENSOR_TRIM_CALIB_VALID_MASK (GENMASK(3, 2) | BIT(7)) 64*421eda10SGuillaume La Roque 65*421eda10SGuillaume La Roque #define TSENSOR_CALIB_OFFSET 1 66*421eda10SGuillaume La Roque #define TSENSOR_CALIB_SHIFT 4 67*421eda10SGuillaume La Roque 68*421eda10SGuillaume La Roque /** 69*421eda10SGuillaume La Roque * struct amlogic_thermal_soc_calib_data 70*421eda10SGuillaume La Roque * @A, B, m, n: calibration parameters 71*421eda10SGuillaume La Roque * This structure is required for configuration of amlogic thermal driver. 72*421eda10SGuillaume La Roque */ 73*421eda10SGuillaume La Roque struct amlogic_thermal_soc_calib_data { 74*421eda10SGuillaume La Roque int A; 75*421eda10SGuillaume La Roque int B; 76*421eda10SGuillaume La Roque int m; 77*421eda10SGuillaume La Roque int n; 78*421eda10SGuillaume La Roque }; 79*421eda10SGuillaume La Roque 80*421eda10SGuillaume La Roque /** 81*421eda10SGuillaume La Roque * struct amlogic_thermal_data 82*421eda10SGuillaume La Roque * @u_efuse_off: register offset to read fused calibration value 83*421eda10SGuillaume La Roque * @calibration_parameters: calibration parameters structure pointer 84*421eda10SGuillaume La Roque * @regmap_config: regmap config for the device 85*421eda10SGuillaume La Roque * This structure is required for configuration of amlogic thermal driver. 86*421eda10SGuillaume La Roque */ 87*421eda10SGuillaume La Roque struct amlogic_thermal_data { 88*421eda10SGuillaume La Roque int u_efuse_off; 89*421eda10SGuillaume La Roque const struct amlogic_thermal_soc_calib_data *calibration_parameters; 90*421eda10SGuillaume La Roque const struct regmap_config *regmap_config; 91*421eda10SGuillaume La Roque }; 92*421eda10SGuillaume La Roque 93*421eda10SGuillaume La Roque struct amlogic_thermal { 94*421eda10SGuillaume La Roque struct platform_device *pdev; 95*421eda10SGuillaume La Roque const struct amlogic_thermal_data *data; 96*421eda10SGuillaume La Roque struct regmap *regmap; 97*421eda10SGuillaume La Roque struct regmap *sec_ao_map; 98*421eda10SGuillaume La Roque struct clk *clk; 99*421eda10SGuillaume La Roque struct thermal_zone_device *tzd; 100*421eda10SGuillaume La Roque u32 trim_info; 101*421eda10SGuillaume La Roque }; 102*421eda10SGuillaume La Roque 103*421eda10SGuillaume La Roque /* 104*421eda10SGuillaume La Roque * Calculate a temperature value from a temperature code. 105*421eda10SGuillaume La Roque * The unit of the temperature is degree milliCelsius. 106*421eda10SGuillaume La Roque */ 107*421eda10SGuillaume La Roque static int amlogic_thermal_code_to_millicelsius(struct amlogic_thermal *pdata, 108*421eda10SGuillaume La Roque int temp_code) 109*421eda10SGuillaume La Roque { 110*421eda10SGuillaume La Roque const struct amlogic_thermal_soc_calib_data *param = 111*421eda10SGuillaume La Roque pdata->data->calibration_parameters; 112*421eda10SGuillaume La Roque int temp; 113*421eda10SGuillaume La Roque s64 factor, Uptat, uefuse; 114*421eda10SGuillaume La Roque 115*421eda10SGuillaume La Roque uefuse = pdata->trim_info & TSENSOR_TRIM_SIGN_MASK ? 116*421eda10SGuillaume La Roque ~(pdata->trim_info & TSENSOR_TRIM_TEMP_MASK) + 1 : 117*421eda10SGuillaume La Roque (pdata->trim_info & TSENSOR_TRIM_TEMP_MASK); 118*421eda10SGuillaume La Roque 119*421eda10SGuillaume La Roque factor = param->n * temp_code; 120*421eda10SGuillaume La Roque factor = div_s64(factor, 100); 121*421eda10SGuillaume La Roque 122*421eda10SGuillaume La Roque Uptat = temp_code * param->m; 123*421eda10SGuillaume La Roque Uptat = div_s64(Uptat, 100); 124*421eda10SGuillaume La Roque Uptat = Uptat * BIT(16); 125*421eda10SGuillaume La Roque Uptat = div_s64(Uptat, BIT(16) + factor); 126*421eda10SGuillaume La Roque 127*421eda10SGuillaume La Roque temp = (Uptat + uefuse) * param->A; 128*421eda10SGuillaume La Roque temp = div_s64(temp, BIT(16)); 129*421eda10SGuillaume La Roque temp = (temp - param->B) * 100; 130*421eda10SGuillaume La Roque 131*421eda10SGuillaume La Roque return temp; 132*421eda10SGuillaume La Roque } 133*421eda10SGuillaume La Roque 134*421eda10SGuillaume La Roque static int amlogic_thermal_initialize(struct amlogic_thermal *pdata) 135*421eda10SGuillaume La Roque { 136*421eda10SGuillaume La Roque int ret = 0; 137*421eda10SGuillaume La Roque int ver; 138*421eda10SGuillaume La Roque 139*421eda10SGuillaume La Roque regmap_read(pdata->sec_ao_map, pdata->data->u_efuse_off, 140*421eda10SGuillaume La Roque &pdata->trim_info); 141*421eda10SGuillaume La Roque 142*421eda10SGuillaume La Roque ver = TSENSOR_TRIM_VERSION(pdata->trim_info); 143*421eda10SGuillaume La Roque 144*421eda10SGuillaume La Roque if ((ver & TSENSOR_TRIM_CALIB_VALID_MASK) == 0) { 145*421eda10SGuillaume La Roque ret = -EINVAL; 146*421eda10SGuillaume La Roque dev_err(&pdata->pdev->dev, 147*421eda10SGuillaume La Roque "tsensor thermal calibration not supported: 0x%x!\n", 148*421eda10SGuillaume La Roque ver); 149*421eda10SGuillaume La Roque } 150*421eda10SGuillaume La Roque 151*421eda10SGuillaume La Roque return ret; 152*421eda10SGuillaume La Roque } 153*421eda10SGuillaume La Roque 154*421eda10SGuillaume La Roque static int amlogic_thermal_enable(struct amlogic_thermal *data) 155*421eda10SGuillaume La Roque { 156*421eda10SGuillaume La Roque int ret; 157*421eda10SGuillaume La Roque 158*421eda10SGuillaume La Roque ret = clk_prepare_enable(data->clk); 159*421eda10SGuillaume La Roque if (ret) 160*421eda10SGuillaume La Roque return ret; 161*421eda10SGuillaume La Roque 162*421eda10SGuillaume La Roque regmap_update_bits(data->regmap, TSENSOR_CFG_REG1, 163*421eda10SGuillaume La Roque TSENSOR_CFG_REG1_ENABLE, TSENSOR_CFG_REG1_ENABLE); 164*421eda10SGuillaume La Roque 165*421eda10SGuillaume La Roque return 0; 166*421eda10SGuillaume La Roque } 167*421eda10SGuillaume La Roque 168*421eda10SGuillaume La Roque static int amlogic_thermal_disable(struct amlogic_thermal *data) 169*421eda10SGuillaume La Roque { 170*421eda10SGuillaume La Roque regmap_update_bits(data->regmap, TSENSOR_CFG_REG1, 171*421eda10SGuillaume La Roque TSENSOR_CFG_REG1_ENABLE, 0); 172*421eda10SGuillaume La Roque clk_disable_unprepare(data->clk); 173*421eda10SGuillaume La Roque 174*421eda10SGuillaume La Roque return 0; 175*421eda10SGuillaume La Roque } 176*421eda10SGuillaume La Roque 177*421eda10SGuillaume La Roque static int amlogic_thermal_get_temp(void *data, int *temp) 178*421eda10SGuillaume La Roque { 179*421eda10SGuillaume La Roque unsigned int tval; 180*421eda10SGuillaume La Roque struct amlogic_thermal *pdata = data; 181*421eda10SGuillaume La Roque 182*421eda10SGuillaume La Roque if (!data) 183*421eda10SGuillaume La Roque return -EINVAL; 184*421eda10SGuillaume La Roque 185*421eda10SGuillaume La Roque regmap_read(pdata->regmap, TSENSOR_STAT0, &tval); 186*421eda10SGuillaume La Roque *temp = 187*421eda10SGuillaume La Roque amlogic_thermal_code_to_millicelsius(pdata, 188*421eda10SGuillaume La Roque tval & TSENSOR_READ_TEMP_MASK); 189*421eda10SGuillaume La Roque 190*421eda10SGuillaume La Roque return 0; 191*421eda10SGuillaume La Roque } 192*421eda10SGuillaume La Roque 193*421eda10SGuillaume La Roque static const struct thermal_zone_of_device_ops amlogic_thermal_ops = { 194*421eda10SGuillaume La Roque .get_temp = amlogic_thermal_get_temp, 195*421eda10SGuillaume La Roque }; 196*421eda10SGuillaume La Roque 197*421eda10SGuillaume La Roque static const struct regmap_config amlogic_thermal_regmap_config_g12a = { 198*421eda10SGuillaume La Roque .reg_bits = 8, 199*421eda10SGuillaume La Roque .val_bits = 32, 200*421eda10SGuillaume La Roque .reg_stride = 4, 201*421eda10SGuillaume La Roque .max_register = TSENSOR_STAT9, 202*421eda10SGuillaume La Roque }; 203*421eda10SGuillaume La Roque 204*421eda10SGuillaume La Roque static const struct amlogic_thermal_soc_calib_data amlogic_thermal_g12a = { 205*421eda10SGuillaume La Roque .A = 9411, 206*421eda10SGuillaume La Roque .B = 3159, 207*421eda10SGuillaume La Roque .m = 424, 208*421eda10SGuillaume La Roque .n = 324, 209*421eda10SGuillaume La Roque }; 210*421eda10SGuillaume La Roque 211*421eda10SGuillaume La Roque static const struct amlogic_thermal_data amlogic_thermal_g12a_cpu_param = { 212*421eda10SGuillaume La Roque .u_efuse_off = 0x128, 213*421eda10SGuillaume La Roque .calibration_parameters = &amlogic_thermal_g12a, 214*421eda10SGuillaume La Roque .regmap_config = &amlogic_thermal_regmap_config_g12a, 215*421eda10SGuillaume La Roque }; 216*421eda10SGuillaume La Roque 217*421eda10SGuillaume La Roque static const struct amlogic_thermal_data amlogic_thermal_g12a_ddr_param = { 218*421eda10SGuillaume La Roque .u_efuse_off = 0xf0, 219*421eda10SGuillaume La Roque .calibration_parameters = &amlogic_thermal_g12a, 220*421eda10SGuillaume La Roque .regmap_config = &amlogic_thermal_regmap_config_g12a, 221*421eda10SGuillaume La Roque }; 222*421eda10SGuillaume La Roque 223*421eda10SGuillaume La Roque static const struct of_device_id of_amlogic_thermal_match[] = { 224*421eda10SGuillaume La Roque { 225*421eda10SGuillaume La Roque .compatible = "amlogic,g12a-ddr-thermal", 226*421eda10SGuillaume La Roque .data = &amlogic_thermal_g12a_ddr_param, 227*421eda10SGuillaume La Roque }, 228*421eda10SGuillaume La Roque { 229*421eda10SGuillaume La Roque .compatible = "amlogic,g12a-cpu-thermal", 230*421eda10SGuillaume La Roque .data = &amlogic_thermal_g12a_cpu_param, 231*421eda10SGuillaume La Roque }, 232*421eda10SGuillaume La Roque { /* sentinel */ } 233*421eda10SGuillaume La Roque }; 234*421eda10SGuillaume La Roque MODULE_DEVICE_TABLE(of, of_amlogic_thermal_match); 235*421eda10SGuillaume La Roque 236*421eda10SGuillaume La Roque static int amlogic_thermal_probe(struct platform_device *pdev) 237*421eda10SGuillaume La Roque { 238*421eda10SGuillaume La Roque struct amlogic_thermal *pdata; 239*421eda10SGuillaume La Roque struct device *dev = &pdev->dev; 240*421eda10SGuillaume La Roque void __iomem *base; 241*421eda10SGuillaume La Roque int ret; 242*421eda10SGuillaume La Roque 243*421eda10SGuillaume La Roque pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 244*421eda10SGuillaume La Roque if (!pdata) 245*421eda10SGuillaume La Roque return -ENOMEM; 246*421eda10SGuillaume La Roque 247*421eda10SGuillaume La Roque pdata->data = of_device_get_match_data(dev); 248*421eda10SGuillaume La Roque pdata->pdev = pdev; 249*421eda10SGuillaume La Roque platform_set_drvdata(pdev, pdata); 250*421eda10SGuillaume La Roque 251*421eda10SGuillaume La Roque base = devm_platform_ioremap_resource(pdev, 0); 252*421eda10SGuillaume La Roque if (IS_ERR(base)) { 253*421eda10SGuillaume La Roque dev_err(dev, "failed to get io address\n"); 254*421eda10SGuillaume La Roque return PTR_ERR(base); 255*421eda10SGuillaume La Roque } 256*421eda10SGuillaume La Roque 257*421eda10SGuillaume La Roque pdata->regmap = devm_regmap_init_mmio(dev, base, 258*421eda10SGuillaume La Roque pdata->data->regmap_config); 259*421eda10SGuillaume La Roque if (IS_ERR(pdata->regmap)) 260*421eda10SGuillaume La Roque return PTR_ERR(pdata->regmap); 261*421eda10SGuillaume La Roque 262*421eda10SGuillaume La Roque pdata->clk = devm_clk_get(dev, NULL); 263*421eda10SGuillaume La Roque if (IS_ERR(pdata->clk)) { 264*421eda10SGuillaume La Roque if (PTR_ERR(pdata->clk) != -EPROBE_DEFER) 265*421eda10SGuillaume La Roque dev_err(dev, "failed to get clock\n"); 266*421eda10SGuillaume La Roque return PTR_ERR(pdata->clk); 267*421eda10SGuillaume La Roque } 268*421eda10SGuillaume La Roque 269*421eda10SGuillaume La Roque pdata->sec_ao_map = syscon_regmap_lookup_by_phandle 270*421eda10SGuillaume La Roque (pdev->dev.of_node, "amlogic,ao-secure"); 271*421eda10SGuillaume La Roque if (IS_ERR(pdata->sec_ao_map)) { 272*421eda10SGuillaume La Roque dev_err(dev, "syscon regmap lookup failed.\n"); 273*421eda10SGuillaume La Roque return PTR_ERR(pdata->sec_ao_map); 274*421eda10SGuillaume La Roque } 275*421eda10SGuillaume La Roque 276*421eda10SGuillaume La Roque pdata->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 277*421eda10SGuillaume La Roque 0, 278*421eda10SGuillaume La Roque pdata, 279*421eda10SGuillaume La Roque &amlogic_thermal_ops); 280*421eda10SGuillaume La Roque if (IS_ERR(pdata->tzd)) { 281*421eda10SGuillaume La Roque ret = PTR_ERR(pdata->tzd); 282*421eda10SGuillaume La Roque dev_err(dev, "Failed to register tsensor: %d\n", ret); 283*421eda10SGuillaume La Roque return ret; 284*421eda10SGuillaume La Roque } 285*421eda10SGuillaume La Roque 286*421eda10SGuillaume La Roque ret = amlogic_thermal_initialize(pdata); 287*421eda10SGuillaume La Roque if (ret) 288*421eda10SGuillaume La Roque return ret; 289*421eda10SGuillaume La Roque 290*421eda10SGuillaume La Roque ret = amlogic_thermal_enable(pdata); 291*421eda10SGuillaume La Roque 292*421eda10SGuillaume La Roque return ret; 293*421eda10SGuillaume La Roque } 294*421eda10SGuillaume La Roque 295*421eda10SGuillaume La Roque static int amlogic_thermal_remove(struct platform_device *pdev) 296*421eda10SGuillaume La Roque { 297*421eda10SGuillaume La Roque struct amlogic_thermal *data = platform_get_drvdata(pdev); 298*421eda10SGuillaume La Roque 299*421eda10SGuillaume La Roque return amlogic_thermal_disable(data); 300*421eda10SGuillaume La Roque } 301*421eda10SGuillaume La Roque 302*421eda10SGuillaume La Roque static int __maybe_unused amlogic_thermal_suspend(struct device *dev) 303*421eda10SGuillaume La Roque { 304*421eda10SGuillaume La Roque struct amlogic_thermal *data = dev_get_drvdata(dev); 305*421eda10SGuillaume La Roque 306*421eda10SGuillaume La Roque return amlogic_thermal_disable(data); 307*421eda10SGuillaume La Roque } 308*421eda10SGuillaume La Roque 309*421eda10SGuillaume La Roque static int __maybe_unused amlogic_thermal_resume(struct device *dev) 310*421eda10SGuillaume La Roque { 311*421eda10SGuillaume La Roque struct amlogic_thermal *data = dev_get_drvdata(dev); 312*421eda10SGuillaume La Roque 313*421eda10SGuillaume La Roque return amlogic_thermal_enable(data); 314*421eda10SGuillaume La Roque } 315*421eda10SGuillaume La Roque 316*421eda10SGuillaume La Roque static SIMPLE_DEV_PM_OPS(amlogic_thermal_pm_ops, 317*421eda10SGuillaume La Roque amlogic_thermal_suspend, amlogic_thermal_resume); 318*421eda10SGuillaume La Roque 319*421eda10SGuillaume La Roque static struct platform_driver amlogic_thermal_driver = { 320*421eda10SGuillaume La Roque .driver = { 321*421eda10SGuillaume La Roque .name = "amlogic_thermal", 322*421eda10SGuillaume La Roque .pm = &amlogic_thermal_pm_ops, 323*421eda10SGuillaume La Roque .of_match_table = of_amlogic_thermal_match, 324*421eda10SGuillaume La Roque }, 325*421eda10SGuillaume La Roque .probe = amlogic_thermal_probe, 326*421eda10SGuillaume La Roque .remove = amlogic_thermal_remove, 327*421eda10SGuillaume La Roque }; 328*421eda10SGuillaume La Roque 329*421eda10SGuillaume La Roque module_platform_driver(amlogic_thermal_driver); 330*421eda10SGuillaume La Roque 331*421eda10SGuillaume La Roque MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>"); 332*421eda10SGuillaume La Roque MODULE_DESCRIPTION("Amlogic thermal driver"); 333*421eda10SGuillaume La Roque MODULE_LICENSE("GPL v2"); 334