1 /* 2 * R-Car Gen3 THS thermal sensor driver 3 * Based on rcar_thermal.c and work from Hien Dang and Khiem Nguyen. 4 * 5 * Copyright (C) 2016 Renesas Electronics Corporation. 6 * Copyright (C) 2016 Sang Engineering 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; version 2 of the License. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 */ 18 #include <linux/delay.h> 19 #include <linux/err.h> 20 #include <linux/interrupt.h> 21 #include <linux/io.h> 22 #include <linux/module.h> 23 #include <linux/mutex.h> 24 #include <linux/of_device.h> 25 #include <linux/platform_device.h> 26 #include <linux/pm_runtime.h> 27 #include <linux/thermal.h> 28 29 /* Register offsets */ 30 #define REG_GEN3_IRQSTR 0x04 31 #define REG_GEN3_IRQMSK 0x08 32 #define REG_GEN3_IRQCTL 0x0C 33 #define REG_GEN3_IRQEN 0x10 34 #define REG_GEN3_IRQTEMP1 0x14 35 #define REG_GEN3_IRQTEMP2 0x18 36 #define REG_GEN3_IRQTEMP3 0x1C 37 #define REG_GEN3_CTSR 0x20 38 #define REG_GEN3_THCTR 0x20 39 #define REG_GEN3_TEMP 0x28 40 #define REG_GEN3_THCODE1 0x50 41 #define REG_GEN3_THCODE2 0x54 42 #define REG_GEN3_THCODE3 0x58 43 44 /* CTSR bits */ 45 #define CTSR_PONM BIT(8) 46 #define CTSR_AOUT BIT(7) 47 #define CTSR_THBGR BIT(5) 48 #define CTSR_VMEN BIT(4) 49 #define CTSR_VMST BIT(1) 50 #define CTSR_THSST BIT(0) 51 52 /* THCTR bits */ 53 #define THCTR_PONM BIT(6) 54 #define THCTR_THSST BIT(0) 55 56 #define CTEMP_MASK 0xFFF 57 58 #define MCELSIUS(temp) ((temp) * 1000) 59 #define GEN3_FUSE_MASK 0xFFF 60 61 #define TSC_MAX_NUM 3 62 63 /* Structure for thermal temperature calculation */ 64 struct equation_coefs { 65 int a1; 66 int b1; 67 int a2; 68 int b2; 69 }; 70 71 struct rcar_gen3_thermal_tsc { 72 void __iomem *base; 73 struct thermal_zone_device *zone; 74 struct equation_coefs coef; 75 struct mutex lock; 76 }; 77 78 struct rcar_gen3_thermal_priv { 79 struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; 80 }; 81 82 struct rcar_gen3_thermal_data { 83 void (*thermal_init)(struct rcar_gen3_thermal_tsc *tsc); 84 }; 85 86 static inline u32 rcar_gen3_thermal_read(struct rcar_gen3_thermal_tsc *tsc, 87 u32 reg) 88 { 89 return ioread32(tsc->base + reg); 90 } 91 92 static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc, 93 u32 reg, u32 data) 94 { 95 iowrite32(data, tsc->base + reg); 96 } 97 98 /* 99 * Linear approximation for temperature 100 * 101 * [reg] = [temp] * a + b => [temp] = ([reg] - b) / a 102 * 103 * The constants a and b are calculated using two triplets of int values PTAT 104 * and THCODE. PTAT and THCODE can either be read from hardware or use hard 105 * coded values from driver. The formula to calculate a and b are taken from 106 * BSP and sparsely documented and understood. 107 * 108 * Examining the linear formula and the formula used to calculate constants a 109 * and b while knowing that the span for PTAT and THCODE values are between 110 * 0x000 and 0xfff the largest integer possible is 0xfff * 0xfff == 0xffe001. 111 * Integer also needs to be signed so that leaves 7 bits for binary 112 * fixed point scaling. 113 */ 114 115 #define FIXPT_SHIFT 7 116 #define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT) 117 #define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b)) 118 #define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT) 119 120 #define RCAR3_THERMAL_GRAN 500 /* mili Celsius */ 121 122 /* no idea where these constants come from */ 123 #define TJ_1 96 124 #define TJ_3 -41 125 126 static void rcar_gen3_thermal_calc_coefs(struct equation_coefs *coef, 127 int *ptat, int *thcode) 128 { 129 int tj_2; 130 131 /* TODO: Find documentation and document constant calculation formula */ 132 133 /* 134 * Division is not scaled in BSP and if scaled it might overflow 135 * the dividend (4095 * 4095 << 14 > INT_MAX) so keep it unscaled 136 */ 137 tj_2 = (FIXPT_INT((ptat[1] - ptat[2]) * 137) 138 / (ptat[0] - ptat[2])) - FIXPT_INT(41); 139 140 coef->a1 = FIXPT_DIV(FIXPT_INT(thcode[1] - thcode[2]), 141 tj_2 - FIXPT_INT(TJ_3)); 142 coef->b1 = FIXPT_INT(thcode[2]) - coef->a1 * TJ_3; 143 144 coef->a2 = FIXPT_DIV(FIXPT_INT(thcode[1] - thcode[0]), 145 tj_2 - FIXPT_INT(TJ_1)); 146 coef->b2 = FIXPT_INT(thcode[0]) - coef->a2 * TJ_1; 147 } 148 149 static int rcar_gen3_thermal_round(int temp) 150 { 151 int result, round_offs; 152 153 round_offs = temp >= 0 ? RCAR3_THERMAL_GRAN / 2 : 154 -RCAR3_THERMAL_GRAN / 2; 155 result = (temp + round_offs) / RCAR3_THERMAL_GRAN; 156 return result * RCAR3_THERMAL_GRAN; 157 } 158 159 static int rcar_gen3_thermal_get_temp(void *devdata, int *temp) 160 { 161 struct rcar_gen3_thermal_tsc *tsc = devdata; 162 int mcelsius, val1, val2; 163 u32 reg; 164 165 /* Read register and convert to mili Celsius */ 166 mutex_lock(&tsc->lock); 167 168 reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK; 169 170 val1 = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b1, tsc->coef.a1); 171 val2 = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b2, tsc->coef.a2); 172 mcelsius = FIXPT_TO_MCELSIUS((val1 + val2) / 2); 173 174 mutex_unlock(&tsc->lock); 175 176 /* Make sure we are inside specifications */ 177 if ((mcelsius < MCELSIUS(-40)) || (mcelsius > MCELSIUS(125))) 178 return -EIO; 179 180 /* Round value to device granularity setting */ 181 *temp = rcar_gen3_thermal_round(mcelsius); 182 183 return 0; 184 } 185 186 static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { 187 .get_temp = rcar_gen3_thermal_get_temp, 188 }; 189 190 static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) 191 { 192 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR); 193 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 0x0); 194 195 usleep_range(1000, 2000); 196 197 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_PONM); 198 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); 199 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 200 CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN); 201 202 usleep_range(100, 200); 203 204 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 205 CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN | 206 CTSR_VMST | CTSR_THSST); 207 208 usleep_range(1000, 2000); 209 } 210 211 static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc) 212 { 213 u32 reg_val; 214 215 reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); 216 reg_val &= ~THCTR_PONM; 217 rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val); 218 219 usleep_range(1000, 2000); 220 221 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); 222 reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); 223 reg_val |= THCTR_THSST; 224 rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val); 225 } 226 227 static const struct rcar_gen3_thermal_data r8a7795_data = { 228 .thermal_init = r8a7795_thermal_init, 229 }; 230 231 static const struct rcar_gen3_thermal_data r8a7796_data = { 232 .thermal_init = r8a7796_thermal_init, 233 }; 234 235 static const struct of_device_id rcar_gen3_thermal_dt_ids[] = { 236 { .compatible = "renesas,r8a7795-thermal", .data = &r8a7795_data}, 237 { .compatible = "renesas,r8a7796-thermal", .data = &r8a7796_data}, 238 {}, 239 }; 240 MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); 241 242 static int rcar_gen3_thermal_remove(struct platform_device *pdev) 243 { 244 struct device *dev = &pdev->dev; 245 246 pm_runtime_put(dev); 247 pm_runtime_disable(dev); 248 249 return 0; 250 } 251 252 static int rcar_gen3_thermal_probe(struct platform_device *pdev) 253 { 254 struct rcar_gen3_thermal_priv *priv; 255 struct device *dev = &pdev->dev; 256 struct resource *res; 257 struct thermal_zone_device *zone; 258 int ret, i; 259 const struct rcar_gen3_thermal_data *match_data = 260 of_device_get_match_data(dev); 261 262 /* default values if FUSEs are missing */ 263 /* TODO: Read values from hardware on supported platforms */ 264 int ptat[3] = { 2351, 1509, 435 }; 265 int thcode[TSC_MAX_NUM][3] = { 266 { 3248, 2800, 2221 }, 267 { 3245, 2795, 2216 }, 268 { 3250, 2805, 2237 }, 269 }; 270 271 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 272 if (!priv) 273 return -ENOMEM; 274 275 platform_set_drvdata(pdev, priv); 276 277 pm_runtime_enable(dev); 278 pm_runtime_get_sync(dev); 279 280 for (i = 0; i < TSC_MAX_NUM; i++) { 281 struct rcar_gen3_thermal_tsc *tsc; 282 283 tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL); 284 if (!tsc) { 285 ret = -ENOMEM; 286 goto error_unregister; 287 } 288 289 res = platform_get_resource(pdev, IORESOURCE_MEM, i); 290 if (!res) 291 break; 292 293 tsc->base = devm_ioremap_resource(dev, res); 294 if (IS_ERR(tsc->base)) { 295 ret = PTR_ERR(tsc->base); 296 goto error_unregister; 297 } 298 299 priv->tscs[i] = tsc; 300 mutex_init(&tsc->lock); 301 302 match_data->thermal_init(tsc); 303 rcar_gen3_thermal_calc_coefs(&tsc->coef, ptat, thcode[i]); 304 305 zone = devm_thermal_zone_of_sensor_register(dev, i, tsc, 306 &rcar_gen3_tz_of_ops); 307 if (IS_ERR(zone)) { 308 dev_err(dev, "Can't register thermal zone\n"); 309 ret = PTR_ERR(zone); 310 goto error_unregister; 311 } 312 tsc->zone = zone; 313 } 314 315 return 0; 316 317 error_unregister: 318 rcar_gen3_thermal_remove(pdev); 319 320 return ret; 321 } 322 323 static struct platform_driver rcar_gen3_thermal_driver = { 324 .driver = { 325 .name = "rcar_gen3_thermal", 326 .of_match_table = rcar_gen3_thermal_dt_ids, 327 }, 328 .probe = rcar_gen3_thermal_probe, 329 .remove = rcar_gen3_thermal_remove, 330 }; 331 module_platform_driver(rcar_gen3_thermal_driver); 332 333 MODULE_LICENSE("GPL v2"); 334 MODULE_DESCRIPTION("R-Car Gen3 THS thermal sensor driver"); 335 MODULE_AUTHOR("Wolfram Sang <wsa+renesas@sang-engineering.com>"); 336