Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | // SPDX-License-Identifier: GPL-2.0 /* * Renesas RZ/G2L TSU Thermal Sensor Driver * * Copyright (C) 2021 Renesas Electronics Corporation */ #include <linux/delay.h> #include <linux/err.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/math.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/thermal.h> #include <linux/units.h> #include "thermal_hwmon.h" #define CTEMP_MASK 0xFFF /* default calibration values, if FUSE values are missing */ #define SW_CALIB0_VAL 3148 #define SW_CALIB1_VAL 503 /* Register offsets */ #define TSU_SM 0x00 #define TSU_ST 0x04 #define TSU_SAD 0x0C #define TSU_SS 0x10 #define OTPTSUTRIM_REG(n) (0x18 + ((n) * 0x4)) #define OTPTSUTRIM_EN_MASK BIT(31) #define OTPTSUTRIM_MASK GENMASK(11, 0) /* Sensor Mode Register(TSU_SM) */ #define TSU_SM_EN_TS BIT(0) #define TSU_SM_ADC_EN_TS BIT(1) #define TSU_SM_NORMAL_MODE (TSU_SM_EN_TS | TSU_SM_ADC_EN_TS) /* TSU_ST bits */ #define TSU_ST_START BIT(0) #define TSU_SS_CONV_RUNNING BIT(0) #define TS_CODE_AVE_SCALE(x) ((x) * 1000000) #define MCELSIUS(temp) ((temp) * MILLIDEGREE_PER_DEGREE) #define TS_CODE_CAP_TIMES 8 /* Total number of ADC data samples */ #define RZG2L_THERMAL_GRAN 500 /* milli Celsius */ #define RZG2L_TSU_SS_TIMEOUT_US 1000 #define CURVATURE_CORRECTION_CONST 13 struct rzg2l_thermal_priv { struct device *dev; void __iomem *base; struct thermal_zone_device *zone; struct reset_control *rstc; u32 calib0, calib1; }; static inline u32 rzg2l_thermal_read(struct rzg2l_thermal_priv *priv, u32 reg) { return ioread32(priv->base + reg); } static inline void rzg2l_thermal_write(struct rzg2l_thermal_priv *priv, u32 reg, u32 data) { iowrite32(data, priv->base + reg); } static int rzg2l_thermal_get_temp(struct thermal_zone_device *tz, int *temp) { struct rzg2l_thermal_priv *priv = thermal_zone_device_priv(tz); u32 result = 0, dsensor, ts_code_ave; int val, i; for (i = 0; i < TS_CODE_CAP_TIMES ; i++) { /* * TSU repeats measurement at 20 microseconds intervals and * automatically updates the results of measurement. As per * the HW manual for measuring temperature we need to read 8 * values consecutively and then take the average. * ts_code_ave = (ts_code[0] + ⋯ + ts_code[7]) / 8 */ result += rzg2l_thermal_read(priv, TSU_SAD) & CTEMP_MASK; usleep_range(20, 30); } ts_code_ave = result / TS_CODE_CAP_TIMES; /* * Calculate actual sensor value by applying curvature correction formula * dsensor = ts_code_ave / (1 + ts_code_ave * 0.000013). Here we are doing * integer calculation by scaling all the values by 1000000. */ dsensor = TS_CODE_AVE_SCALE(ts_code_ave) / (TS_CODE_AVE_SCALE(1) + (ts_code_ave * CURVATURE_CORRECTION_CONST)); /* * The temperature Tj is calculated by the formula * Tj = (dsensor − calib1) * 165/ (calib0 − calib1) − 40 * where calib0 and calib1 are the calibration values. */ val = ((dsensor - priv->calib1) * (MCELSIUS(165) / (priv->calib0 - priv->calib1))) - MCELSIUS(40); *temp = roundup(val, RZG2L_THERMAL_GRAN); return 0; } static const struct thermal_zone_device_ops rzg2l_tz_of_ops = { .get_temp = rzg2l_thermal_get_temp, }; static int rzg2l_thermal_init(struct rzg2l_thermal_priv *priv) { u32 reg_val; rzg2l_thermal_write(priv, TSU_SM, TSU_SM_NORMAL_MODE); rzg2l_thermal_write(priv, TSU_ST, 0); /* * Before setting the START bit, TSU should be in normal operating * mode. As per the HW manual, it will take 60 µs to place the TSU * into normal operating mode. */ usleep_range(60, 80); reg_val = rzg2l_thermal_read(priv, TSU_ST); reg_val |= TSU_ST_START; rzg2l_thermal_write(priv, TSU_ST, reg_val); return readl_poll_timeout(priv->base + TSU_SS, reg_val, reg_val == TSU_SS_CONV_RUNNING, 50, RZG2L_TSU_SS_TIMEOUT_US); } static void rzg2l_thermal_reset_assert_pm_disable_put(struct platform_device *pdev) { struct rzg2l_thermal_priv *priv = dev_get_drvdata(&pdev->dev); pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); reset_control_assert(priv->rstc); } static void rzg2l_thermal_remove(struct platform_device *pdev) { struct rzg2l_thermal_priv *priv = dev_get_drvdata(&pdev->dev); thermal_remove_hwmon_sysfs(priv->zone); rzg2l_thermal_reset_assert_pm_disable_put(pdev); } static int rzg2l_thermal_probe(struct platform_device *pdev) { struct thermal_zone_device *zone; struct rzg2l_thermal_priv *priv; struct device *dev = &pdev->dev; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); priv->dev = dev; priv->rstc = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(priv->rstc)) return dev_err_probe(dev, PTR_ERR(priv->rstc), "failed to get cpg reset"); ret = reset_control_deassert(priv->rstc); if (ret) return dev_err_probe(dev, ret, "failed to deassert"); pm_runtime_enable(dev); pm_runtime_get_sync(dev); priv->calib0 = rzg2l_thermal_read(priv, OTPTSUTRIM_REG(0)); if (priv->calib0 & OTPTSUTRIM_EN_MASK) priv->calib0 &= OTPTSUTRIM_MASK; else priv->calib0 = SW_CALIB0_VAL; priv->calib1 = rzg2l_thermal_read(priv, OTPTSUTRIM_REG(1)); if (priv->calib1 & OTPTSUTRIM_EN_MASK) priv->calib1 &= OTPTSUTRIM_MASK; else priv->calib1 = SW_CALIB1_VAL; platform_set_drvdata(pdev, priv); ret = rzg2l_thermal_init(priv); if (ret) { dev_err(dev, "Failed to start TSU"); goto err; } zone = devm_thermal_of_zone_register(dev, 0, priv, &rzg2l_tz_of_ops); if (IS_ERR(zone)) { dev_err(dev, "Can't register thermal zone"); ret = PTR_ERR(zone); goto err; } priv->zone = zone; ret = thermal_add_hwmon_sysfs(priv->zone); if (ret) goto err; dev_dbg(dev, "TSU probed with %s calibration values", rzg2l_thermal_read(priv, OTPTSUTRIM_REG(0)) ? "hw" : "sw"); return 0; err: rzg2l_thermal_reset_assert_pm_disable_put(pdev); return ret; } static const struct of_device_id rzg2l_thermal_dt_ids[] = { { .compatible = "renesas,rzg2l-tsu", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzg2l_thermal_dt_ids); static struct platform_driver rzg2l_thermal_driver = { .driver = { .name = "rzg2l_thermal", .of_match_table = rzg2l_thermal_dt_ids, }, .probe = rzg2l_thermal_probe, .remove_new = rzg2l_thermal_remove, }; module_platform_driver(rzg2l_thermal_driver); MODULE_DESCRIPTION("Renesas RZ/G2L TSU Thermal Sensor Driver"); MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); MODULE_LICENSE("GPL v2"); |