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