Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

thermal: sti: Introduce ST Thermal core code

This core is shared by both ST's 'memory mapped' and
'system configuration register' based Thermal controllers.

Signed-off-by: Ajit Pal Singh <ajitpal.singh@st.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>

authored by

Lee Jones and committed by
Zhang Rui
60aef7ce a5635912

+428
+5
drivers/thermal/Kconfig
··· 243 243 source "drivers/thermal/samsung/Kconfig" 244 244 endmenu 245 245 246 + menu "STMicroelectronics thermal drivers" 247 + depends on ARCH_STI && OF 248 + source "drivers/thermal/st/Kconfig" 249 + endmenu 250 + 246 251 endif
+1
drivers/thermal/Makefile
··· 32 32 obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o 33 33 obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ 34 34 obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o 35 + obj-$(CONFIG_ST_THERMAL) += st/
+4
drivers/thermal/st/Kconfig
··· 1 + config ST_THERMAL 2 + tristate "Thermal sensors on STMicroelectronics STi series of SoCs" 3 + help 4 + Support for thermal sensors on STMicroelectronics STi series of SoCs.
+1
drivers/thermal/st/Makefile
··· 1 + obj-$(CONFIG_ST_THERMAL) := st_thermal.o
+313
drivers/thermal/st/st_thermal.c
··· 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 + */ 28 + 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 + 53 + static 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 + 73 + static 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 + 86 + static 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*/ 114 + static 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 + 143 + static 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 + 161 + static 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 + 179 + static 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 + 185 + int 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 + 260 + sensor_off: 261 + st_thermal_sensor_off(sensor); 262 + 263 + return ret; 264 + } 265 + EXPORT_SYMBOL_GPL(st_thermal_register); 266 + 267 + int 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 + } 276 + EXPORT_SYMBOL_GPL(st_thermal_unregister); 277 + 278 + static 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 + 286 + static 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 + SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume); 309 + EXPORT_SYMBOL_GPL(st_thermal_pm_ops); 310 + 311 + MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>"); 312 + MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver"); 313 + MODULE_LICENSE("GPL v2");
+104
drivers/thermal/st/st_thermal.h
··· 1 + /* 2 + * ST Thermal Sensor Driver for STi series of SoCs 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 + #ifndef __STI_THERMAL_SYSCFG_H 14 + #define __STI_THERMAL_SYSCFG_H 15 + 16 + #include <linux/interrupt.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/regmap.h> 19 + #include <linux/thermal.h> 20 + 21 + enum st_thermal_regfield_ids { 22 + INT_THRESH_HI = 0, /* Top two regfield IDs are mutually exclusive */ 23 + TEMP_PWR = 0, 24 + DCORRECT, 25 + OVERFLOW, 26 + DATA, 27 + INT_ENABLE, 28 + 29 + MAX_REGFIELDS 30 + }; 31 + 32 + /* Thermal sensor power states */ 33 + enum st_thermal_power_state { 34 + POWER_OFF = 0, 35 + POWER_ON 36 + }; 37 + 38 + struct st_thermal_sensor; 39 + 40 + /** 41 + * Description of private thermal sensor ops. 42 + * 43 + * @power_ctrl: Function for powering on/off a sensor. Clock to the 44 + * sensor is also controlled from this function. 45 + * @alloc_regfields: Allocate regmap register fields, specific to a sensor. 46 + * @do_memmap_regmap: Memory map the thermal register space and init regmap 47 + * instance or find regmap instance. 48 + * @register_irq: Register an interrupt handler for a sensor. 49 + */ 50 + struct st_thermal_sensor_ops { 51 + int (*power_ctrl)(struct st_thermal_sensor *, enum st_thermal_power_state); 52 + int (*alloc_regfields)(struct st_thermal_sensor *); 53 + int (*regmap_init)(struct st_thermal_sensor *); 54 + int (*register_enable_irq)(struct st_thermal_sensor *); 55 + int (*enable_irq)(struct st_thermal_sensor *); 56 + }; 57 + 58 + /** 59 + * Description of thermal driver compatible data. 60 + * 61 + * @reg_fields: Pointer to the regfields array for a sensor. 62 + * @sys_compat: Pointer to the syscon node compatible string. 63 + * @ops: Pointer to private thermal ops for a sensor. 64 + * @calibration_val: Default calibration value to be written to the DCORRECT 65 + * register field for a sensor. 66 + * @temp_adjust_val: Value to be added/subtracted from the data read from 67 + * the sensor. If value needs to be added please provide a 68 + * positive value and if it is to be subtracted please 69 + * provide a negative value. 70 + * @crit_temp: The temperature beyond which the SoC should be shutdown 71 + * to prevent damage. 72 + */ 73 + struct st_thermal_compat_data { 74 + char *sys_compat; 75 + const struct reg_field *reg_fields; 76 + const struct st_thermal_sensor_ops *ops; 77 + unsigned int calibration_val; 78 + int temp_adjust_val; 79 + int crit_temp; 80 + }; 81 + 82 + struct st_thermal_sensor { 83 + struct device *dev; 84 + struct thermal_zone_device *thermal_dev; 85 + const struct st_thermal_sensor_ops *ops; 86 + const struct st_thermal_compat_data *cdata; 87 + struct clk *clk; 88 + struct regmap *regmap; 89 + struct regmap_field *pwr; 90 + struct regmap_field *dcorrect; 91 + struct regmap_field *overflow; 92 + struct regmap_field *temp_data; 93 + struct regmap_field *int_thresh_hi; 94 + struct regmap_field *int_enable; 95 + int irq; 96 + void __iomem *mmio_base; 97 + }; 98 + 99 + extern int st_thermal_register(struct platform_device *pdev, 100 + const struct of_device_id *st_thermal_of_match); 101 + extern int st_thermal_unregister(struct platform_device *pdev); 102 + extern const struct dev_pm_ops st_thermal_pm_ops; 103 + 104 + #endif /* __STI_RESET_SYSCFG_H */