brintos

brintos / linux-shallow public Read only

0
0
Text · 5.9 KiB · a14a37d Raw
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