268 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * ST Thermal Sensor Driver core routines4 * Author: Ajit Pal Singh <ajitpal.singh@st.com>5 *6 * Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited7 */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#include "../thermal_hwmon.h"16 17/* The Thermal Framework expects millidegrees */18#define mcelsius(temp) ((temp) * 1000)19 20/*21 * Function to allocate regfields which are common22 * between syscfg and memory mapped based sensors23 */24static int st_thermal_alloc_regfields(struct st_thermal_sensor *sensor)25{26 struct device *dev = sensor->dev;27 struct regmap *regmap = sensor->regmap;28 const struct reg_field *reg_fields = sensor->cdata->reg_fields;29 30 sensor->dcorrect = devm_regmap_field_alloc(dev, regmap,31 reg_fields[DCORRECT]);32 33 sensor->overflow = devm_regmap_field_alloc(dev, regmap,34 reg_fields[OVERFLOW]);35 36 sensor->temp_data = devm_regmap_field_alloc(dev, regmap,37 reg_fields[DATA]);38 39 if (IS_ERR(sensor->dcorrect) ||40 IS_ERR(sensor->overflow) ||41 IS_ERR(sensor->temp_data)) {42 dev_err(dev, "failed to allocate common regfields\n");43 return -EINVAL;44 }45 46 return sensor->ops->alloc_regfields(sensor);47}48 49static int st_thermal_sensor_on(struct st_thermal_sensor *sensor)50{51 int ret;52 struct device *dev = sensor->dev;53 54 ret = clk_prepare_enable(sensor->clk);55 if (ret) {56 dev_err(dev, "failed to enable clk\n");57 return ret;58 }59 60 ret = sensor->ops->power_ctrl(sensor, POWER_ON);61 if (ret) {62 dev_err(dev, "failed to power on sensor\n");63 clk_disable_unprepare(sensor->clk);64 }65 66 return ret;67}68 69static int st_thermal_sensor_off(struct st_thermal_sensor *sensor)70{71 int ret;72 73 ret = sensor->ops->power_ctrl(sensor, POWER_OFF);74 if (ret)75 return ret;76 77 clk_disable_unprepare(sensor->clk);78 79 return 0;80}81 82static int st_thermal_calibration(struct st_thermal_sensor *sensor)83{84 int ret;85 unsigned int val;86 struct device *dev = sensor->dev;87 88 /* Check if sensor calibration data is already written */89 ret = regmap_field_read(sensor->dcorrect, &val);90 if (ret) {91 dev_err(dev, "failed to read calibration data\n");92 return ret;93 }94 95 if (!val) {96 /*97 * Sensor calibration value not set by bootloader,98 * default calibration data to be used99 */100 ret = regmap_field_write(sensor->dcorrect,101 sensor->cdata->calibration_val);102 if (ret)103 dev_err(dev, "failed to set calibration data\n");104 }105 106 return ret;107}108 109/* Callback to get temperature from HW*/110static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature)111{112 struct st_thermal_sensor *sensor = thermal_zone_device_priv(th);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 *temperature = temp;131 132 return 0;133}134 135static struct thermal_zone_device_ops st_tz_ops = {136 .get_temp = st_thermal_get_temp,137};138 139int st_thermal_register(struct platform_device *pdev,140 const struct of_device_id *st_thermal_of_match)141{142 struct st_thermal_sensor *sensor;143 struct device *dev = &pdev->dev;144 struct device_node *np = dev->of_node;145 const struct of_device_id *match;146 147 int ret;148 149 if (!np) {150 dev_err(dev, "device tree node not found\n");151 return -EINVAL;152 }153 154 sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);155 if (!sensor)156 return -ENOMEM;157 158 sensor->dev = dev;159 160 match = of_match_device(st_thermal_of_match, dev);161 if (!(match && match->data))162 return -EINVAL;163 164 sensor->cdata = match->data;165 if (!sensor->cdata->ops)166 return -EINVAL;167 168 sensor->ops = sensor->cdata->ops;169 170 ret = (sensor->ops->regmap_init)(sensor);171 if (ret)172 return ret;173 174 ret = st_thermal_alloc_regfields(sensor);175 if (ret)176 return ret;177 178 sensor->clk = devm_clk_get(dev, "thermal");179 if (IS_ERR(sensor->clk)) {180 dev_err(dev, "failed to fetch clock\n");181 return PTR_ERR(sensor->clk);182 }183 184 if (sensor->ops->register_enable_irq) {185 ret = sensor->ops->register_enable_irq(sensor);186 if (ret)187 return ret;188 }189 190 ret = st_thermal_sensor_on(sensor);191 if (ret)192 return ret;193 194 ret = st_thermal_calibration(sensor);195 if (ret)196 goto sensor_off;197 198 sensor->thermal_dev =199 devm_thermal_of_zone_register(dev, 0, sensor, &st_tz_ops);200 if (IS_ERR(sensor->thermal_dev)) {201 dev_err(dev, "failed to register thermal of zone\n");202 ret = PTR_ERR(sensor->thermal_dev);203 goto sensor_off;204 }205 206 platform_set_drvdata(pdev, sensor);207 208 /*209 * devm_thermal_of_zone_register() doesn't enable hwmon by default210 * Enable it here211 */212 devm_thermal_add_hwmon_sysfs(dev, sensor->thermal_dev);213 214 return 0;215 216sensor_off:217 st_thermal_sensor_off(sensor);218 219 return ret;220}221EXPORT_SYMBOL_GPL(st_thermal_register);222 223void st_thermal_unregister(struct platform_device *pdev)224{225 struct st_thermal_sensor *sensor = platform_get_drvdata(pdev);226 227 st_thermal_sensor_off(sensor);228 thermal_remove_hwmon_sysfs(sensor->thermal_dev);229 devm_thermal_of_zone_unregister(sensor->dev, sensor->thermal_dev);230}231EXPORT_SYMBOL_GPL(st_thermal_unregister);232 233static int st_thermal_suspend(struct device *dev)234{235 struct st_thermal_sensor *sensor = dev_get_drvdata(dev);236 237 return st_thermal_sensor_off(sensor);238}239 240static int st_thermal_resume(struct device *dev)241{242 int ret;243 struct st_thermal_sensor *sensor = dev_get_drvdata(dev);244 245 ret = st_thermal_sensor_on(sensor);246 if (ret)247 return ret;248 249 ret = st_thermal_calibration(sensor);250 if (ret)251 return ret;252 253 if (sensor->ops->enable_irq) {254 ret = sensor->ops->enable_irq(sensor);255 if (ret)256 return ret;257 }258 259 return 0;260}261 262DEFINE_SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume);263EXPORT_SYMBOL_GPL(st_thermal_pm_ops);264 265MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>");266MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver");267MODULE_LICENSE("GPL v2");268