st_thermal.c (6924B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * ST Thermal Sensor Driver core routines 4 * Author: Ajit Pal Singh <ajitpal.singh@st.com> 5 * 6 * Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited 7 */ 8 9#include <linux/clk.h> 10#include <linux/module.h> 11#include <linux/of.h> 12#include <linux/of_device.h> 13 14#include "st_thermal.h" 15 16/* The Thermal Framework expects millidegrees */ 17#define mcelsius(temp) ((temp) * 1000) 18 19/* 20 * Function to allocate regfields which are common 21 * between syscfg and memory mapped based sensors 22 */ 23static int st_thermal_alloc_regfields(struct st_thermal_sensor *sensor) 24{ 25 struct device *dev = sensor->dev; 26 struct regmap *regmap = sensor->regmap; 27 const struct reg_field *reg_fields = sensor->cdata->reg_fields; 28 29 sensor->dcorrect = devm_regmap_field_alloc(dev, regmap, 30 reg_fields[DCORRECT]); 31 32 sensor->overflow = devm_regmap_field_alloc(dev, regmap, 33 reg_fields[OVERFLOW]); 34 35 sensor->temp_data = devm_regmap_field_alloc(dev, regmap, 36 reg_fields[DATA]); 37 38 if (IS_ERR(sensor->dcorrect) || 39 IS_ERR(sensor->overflow) || 40 IS_ERR(sensor->temp_data)) { 41 dev_err(dev, "failed to allocate common regfields\n"); 42 return -EINVAL; 43 } 44 45 return sensor->ops->alloc_regfields(sensor); 46} 47 48static int st_thermal_sensor_on(struct st_thermal_sensor *sensor) 49{ 50 int ret; 51 struct device *dev = sensor->dev; 52 53 ret = clk_prepare_enable(sensor->clk); 54 if (ret) { 55 dev_err(dev, "failed to enable clk\n"); 56 return ret; 57 } 58 59 ret = sensor->ops->power_ctrl(sensor, POWER_ON); 60 if (ret) { 61 dev_err(dev, "failed to power on sensor\n"); 62 clk_disable_unprepare(sensor->clk); 63 } 64 65 return ret; 66} 67 68static int st_thermal_sensor_off(struct st_thermal_sensor *sensor) 69{ 70 int ret; 71 72 ret = sensor->ops->power_ctrl(sensor, POWER_OFF); 73 if (ret) 74 return ret; 75 76 clk_disable_unprepare(sensor->clk); 77 78 return 0; 79} 80 81static int st_thermal_calibration(struct st_thermal_sensor *sensor) 82{ 83 int ret; 84 unsigned int val; 85 struct device *dev = sensor->dev; 86 87 /* Check if sensor calibration data is already written */ 88 ret = regmap_field_read(sensor->dcorrect, &val); 89 if (ret) { 90 dev_err(dev, "failed to read calibration data\n"); 91 return ret; 92 } 93 94 if (!val) { 95 /* 96 * Sensor calibration value not set by bootloader, 97 * default calibration data to be used 98 */ 99 ret = regmap_field_write(sensor->dcorrect, 100 sensor->cdata->calibration_val); 101 if (ret) 102 dev_err(dev, "failed to set calibration data\n"); 103 } 104 105 return ret; 106} 107 108/* Callback to get temperature from HW*/ 109static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature) 110{ 111 struct st_thermal_sensor *sensor = th->devdata; 112 struct device *dev = sensor->dev; 113 unsigned int temp; 114 unsigned int overflow; 115 int ret; 116 117 ret = regmap_field_read(sensor->overflow, &overflow); 118 if (ret) 119 return ret; 120 if (overflow) 121 return -EIO; 122 123 ret = regmap_field_read(sensor->temp_data, &temp); 124 if (ret) 125 return ret; 126 127 temp += sensor->cdata->temp_adjust_val; 128 temp = mcelsius(temp); 129 130 dev_dbg(dev, "temperature: %d\n", temp); 131 132 *temperature = temp; 133 134 return 0; 135} 136 137static int st_thermal_get_trip_type(struct thermal_zone_device *th, 138 int trip, enum thermal_trip_type *type) 139{ 140 struct st_thermal_sensor *sensor = th->devdata; 141 struct device *dev = sensor->dev; 142 143 switch (trip) { 144 case 0: 145 *type = THERMAL_TRIP_CRITICAL; 146 break; 147 default: 148 dev_err(dev, "invalid trip point\n"); 149 return -EINVAL; 150 } 151 152 return 0; 153} 154 155static int st_thermal_get_trip_temp(struct thermal_zone_device *th, 156 int trip, int *temp) 157{ 158 struct st_thermal_sensor *sensor = th->devdata; 159 struct device *dev = sensor->dev; 160 161 switch (trip) { 162 case 0: 163 *temp = mcelsius(sensor->cdata->crit_temp); 164 break; 165 default: 166 dev_err(dev, "Invalid trip point\n"); 167 return -EINVAL; 168 } 169 170 return 0; 171} 172 173static struct thermal_zone_device_ops st_tz_ops = { 174 .get_temp = st_thermal_get_temp, 175 .get_trip_type = st_thermal_get_trip_type, 176 .get_trip_temp = st_thermal_get_trip_temp, 177}; 178 179int st_thermal_register(struct platform_device *pdev, 180 const struct of_device_id *st_thermal_of_match) 181{ 182 struct st_thermal_sensor *sensor; 183 struct device *dev = &pdev->dev; 184 struct device_node *np = dev->of_node; 185 const struct of_device_id *match; 186 187 int polling_delay; 188 int ret; 189 190 if (!np) { 191 dev_err(dev, "device tree node not found\n"); 192 return -EINVAL; 193 } 194 195 sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); 196 if (!sensor) 197 return -ENOMEM; 198 199 sensor->dev = dev; 200 201 match = of_match_device(st_thermal_of_match, dev); 202 if (!(match && match->data)) 203 return -EINVAL; 204 205 sensor->cdata = match->data; 206 if (!sensor->cdata->ops) 207 return -EINVAL; 208 209 sensor->ops = sensor->cdata->ops; 210 211 ret = (sensor->ops->regmap_init)(sensor); 212 if (ret) 213 return ret; 214 215 ret = st_thermal_alloc_regfields(sensor); 216 if (ret) 217 return ret; 218 219 sensor->clk = devm_clk_get(dev, "thermal"); 220 if (IS_ERR(sensor->clk)) { 221 dev_err(dev, "failed to fetch clock\n"); 222 return PTR_ERR(sensor->clk); 223 } 224 225 if (sensor->ops->register_enable_irq) { 226 ret = sensor->ops->register_enable_irq(sensor); 227 if (ret) 228 return ret; 229 } 230 231 ret = st_thermal_sensor_on(sensor); 232 if (ret) 233 return ret; 234 235 ret = st_thermal_calibration(sensor); 236 if (ret) 237 goto sensor_off; 238 239 polling_delay = sensor->ops->register_enable_irq ? 0 : 1000; 240 241 sensor->thermal_dev = 242 thermal_zone_device_register(dev_name(dev), 1, 0, sensor, 243 &st_tz_ops, NULL, 0, polling_delay); 244 if (IS_ERR(sensor->thermal_dev)) { 245 dev_err(dev, "failed to register thermal zone device\n"); 246 ret = PTR_ERR(sensor->thermal_dev); 247 goto sensor_off; 248 } 249 ret = thermal_zone_device_enable(sensor->thermal_dev); 250 if (ret) 251 goto tzd_unregister; 252 253 platform_set_drvdata(pdev, sensor); 254 255 return 0; 256 257tzd_unregister: 258 thermal_zone_device_unregister(sensor->thermal_dev); 259sensor_off: 260 st_thermal_sensor_off(sensor); 261 262 return ret; 263} 264EXPORT_SYMBOL_GPL(st_thermal_register); 265 266int st_thermal_unregister(struct platform_device *pdev) 267{ 268 struct st_thermal_sensor *sensor = platform_get_drvdata(pdev); 269 270 st_thermal_sensor_off(sensor); 271 thermal_zone_device_unregister(sensor->thermal_dev); 272 273 return 0; 274} 275EXPORT_SYMBOL_GPL(st_thermal_unregister); 276 277#ifdef CONFIG_PM_SLEEP 278static int st_thermal_suspend(struct device *dev) 279{ 280 struct st_thermal_sensor *sensor = dev_get_drvdata(dev); 281 282 return st_thermal_sensor_off(sensor); 283} 284 285static int st_thermal_resume(struct device *dev) 286{ 287 int ret; 288 struct st_thermal_sensor *sensor = dev_get_drvdata(dev); 289 290 ret = st_thermal_sensor_on(sensor); 291 if (ret) 292 return ret; 293 294 ret = st_thermal_calibration(sensor); 295 if (ret) 296 return ret; 297 298 if (sensor->ops->enable_irq) { 299 ret = sensor->ops->enable_irq(sensor); 300 if (ret) 301 return ret; 302 } 303 304 return 0; 305} 306#endif 307 308SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume); 309EXPORT_SYMBOL_GPL(st_thermal_pm_ops); 310 311MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>"); 312MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver"); 313MODULE_LICENSE("GPL v2");