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, 115 unsigned long *temperature) 116{ 117 struct st_thermal_sensor *sensor = th->devdata; 118 struct device *dev = sensor->dev; 119 unsigned int temp; 120 unsigned int overflow; 121 int ret; 122 123 ret = regmap_field_read(sensor->overflow, &overflow); 124 if (ret) 125 return ret; 126 if (overflow) 127 return -EIO; 128 129 ret = regmap_field_read(sensor->temp_data, &temp); 130 if (ret) 131 return ret; 132 133 temp += sensor->cdata->temp_adjust_val; 134 temp = mcelsius(temp); 135 136 dev_dbg(dev, "temperature: %d\n", temp); 137 138 *temperature = temp; 139 140 return 0; 141} 142 143static int st_thermal_get_trip_type(struct thermal_zone_device *th, 144 int trip, enum thermal_trip_type *type) 145{ 146 struct st_thermal_sensor *sensor = th->devdata; 147 struct device *dev = sensor->dev; 148 149 switch (trip) { 150 case 0: 151 *type = THERMAL_TRIP_CRITICAL; 152 break; 153 default: 154 dev_err(dev, "invalid trip point\n"); 155 return -EINVAL; 156 } 157 158 return 0; 159} 160 161static int st_thermal_get_trip_temp(struct thermal_zone_device *th, 162 int trip, unsigned long *temp) 163{ 164 struct st_thermal_sensor *sensor = th->devdata; 165 struct device *dev = sensor->dev; 166 167 switch (trip) { 168 case 0: 169 *temp = mcelsius(sensor->cdata->crit_temp); 170 break; 171 default: 172 dev_err(dev, "Invalid trip point\n"); 173 return -EINVAL; 174 } 175 176 return 0; 177} 178 179static struct thermal_zone_device_ops st_tz_ops = { 180 .get_temp = st_thermal_get_temp, 181 .get_trip_type = st_thermal_get_trip_type, 182 .get_trip_temp = st_thermal_get_trip_temp, 183}; 184 185int st_thermal_register(struct platform_device *pdev, 186 const struct of_device_id *st_thermal_of_match) 187{ 188 struct st_thermal_sensor *sensor; 189 struct device *dev = &pdev->dev; 190 struct device_node *np = dev->of_node; 191 const struct of_device_id *match; 192 193 int polling_delay; 194 int ret; 195 196 if (!np) { 197 dev_err(dev, "device tree node not found\n"); 198 return -EINVAL; 199 } 200 201 sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); 202 if (!sensor) 203 return -ENOMEM; 204 205 sensor->dev = dev; 206 207 match = of_match_device(st_thermal_of_match, dev); 208 if (!(match && match->data)) 209 return -EINVAL; 210 211 sensor->cdata = match->data; 212 if (!sensor->cdata->ops) 213 return -EINVAL; 214 215 sensor->ops = sensor->cdata->ops; 216 217 ret = sensor->ops->regmap_init(sensor); 218 if (ret) 219 return ret; 220 221 ret = st_thermal_alloc_regfields(sensor); 222 if (ret) 223 return ret; 224 225 sensor->clk = devm_clk_get(dev, "thermal"); 226 if (IS_ERR(sensor->clk)) { 227 dev_err(dev, "failed to fetch clock\n"); 228 return PTR_ERR(sensor->clk); 229 } 230 231 if (sensor->ops->register_enable_irq) { 232 ret = sensor->ops->register_enable_irq(sensor); 233 if (ret) 234 return ret; 235 } 236 237 ret = st_thermal_sensor_on(sensor); 238 if (ret) 239 return ret; 240 241 ret = st_thermal_calibration(sensor); 242 if (ret) 243 goto sensor_off; 244 245 polling_delay = sensor->ops->register_enable_irq ? 0 : 1000; 246 247 sensor->thermal_dev = 248 thermal_zone_device_register(dev_name(dev), 1, 0, sensor, 249 &st_tz_ops, NULL, 0, polling_delay); 250 if (IS_ERR(sensor->thermal_dev)) { 251 dev_err(dev, "failed to register thermal zone device\n"); 252 ret = PTR_ERR(sensor->thermal_dev); 253 goto sensor_off; 254 } 255 256 platform_set_drvdata(pdev, sensor); 257 258 return 0; 259 260sensor_off: 261 st_thermal_sensor_off(sensor); 262 263 return ret; 264} 265EXPORT_SYMBOL_GPL(st_thermal_register); 266 267int st_thermal_unregister(struct platform_device *pdev) 268{ 269 struct st_thermal_sensor *sensor = platform_get_drvdata(pdev); 270 271 st_thermal_sensor_off(sensor); 272 thermal_zone_device_unregister(sensor->thermal_dev); 273 274 return 0; 275} 276EXPORT_SYMBOL_GPL(st_thermal_unregister); 277 278#ifdef CONFIG_PM_SLEEP 279static int st_thermal_suspend(struct device *dev) 280{ 281 struct platform_device *pdev = to_platform_device(dev); 282 struct st_thermal_sensor *sensor = platform_get_drvdata(pdev); 283 284 return st_thermal_sensor_off(sensor); 285} 286 287static int st_thermal_resume(struct device *dev) 288{ 289 int ret; 290 struct platform_device *pdev = to_platform_device(dev); 291 struct st_thermal_sensor *sensor = platform_get_drvdata(pdev); 292 293 ret = st_thermal_sensor_on(sensor); 294 if (ret) 295 return ret; 296 297 ret = st_thermal_calibration(sensor); 298 if (ret) 299 return ret; 300 301 if (sensor->ops->enable_irq) { 302 ret = sensor->ops->enable_irq(sensor); 303 if (ret) 304 return ret; 305 } 306 307 return 0; 308} 309#endif 310 311SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume); 312EXPORT_SYMBOL_GPL(st_thermal_pm_ops); 313 314MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>"); 315MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver"); 316MODULE_LICENSE("GPL v2"); 317