brintos

brintos / linux-shallow public Read only

0
0
Text · 4.1 KiB · ee3d0aa Raw
178 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Generic ADC thermal driver4 *5 * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved.6 *7 * Author: Laxman Dewangan <ldewangan@nvidia.com>8 */9#include <linux/iio/consumer.h>10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/platform_device.h>13#include <linux/slab.h>14#include <linux/thermal.h>15 16#include "thermal_hwmon.h"17 18struct gadc_thermal_info {19	struct device *dev;20	struct thermal_zone_device *tz_dev;21	struct iio_channel *channel;22	s32 *lookup_table;23	int nlookup_table;24};25 26static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val)27{28	int temp, temp_hi, temp_lo, adc_hi, adc_lo;29	int i;30 31	if (!gti->lookup_table)32		return val;33 34	for (i = 0; i < gti->nlookup_table; i++) {35		if (val >= gti->lookup_table[2 * i + 1])36			break;37	}38 39	if (i == 0) {40		temp = gti->lookup_table[0];41	} else if (i >= gti->nlookup_table) {42		temp = gti->lookup_table[2 * (gti->nlookup_table - 1)];43	} else {44		adc_hi = gti->lookup_table[2 * i - 1];45		adc_lo = gti->lookup_table[2 * i + 1];46 47		temp_hi = gti->lookup_table[2 * i - 2];48		temp_lo = gti->lookup_table[2 * i];49 50		temp = temp_hi + mult_frac(temp_lo - temp_hi, val - adc_hi,51					   adc_lo - adc_hi);52	}53 54	return temp;55}56 57static int gadc_thermal_get_temp(struct thermal_zone_device *tz, int *temp)58{59	struct gadc_thermal_info *gti = thermal_zone_device_priv(tz);60	int val;61	int ret;62 63	ret = iio_read_channel_processed(gti->channel, &val);64	if (ret < 0)65		return ret;66 67	*temp = gadc_thermal_adc_to_temp(gti, val);68 69	return 0;70}71 72static const struct thermal_zone_device_ops gadc_thermal_ops = {73	.get_temp = gadc_thermal_get_temp,74};75 76static int gadc_thermal_read_linear_lookup_table(struct device *dev,77						 struct gadc_thermal_info *gti)78{79	struct device_node *np = dev->of_node;80	enum iio_chan_type chan_type;81	int ntable;82	int ret;83 84	ntable = of_property_count_elems_of_size(np, "temperature-lookup-table",85						 sizeof(u32));86	if (ntable <= 0) {87		ret = iio_get_channel_type(gti->channel, &chan_type);88		if (ret || chan_type != IIO_TEMP)89			dev_notice(dev,90				   "no lookup table, assuming DAC channel returns milliCelcius\n");91		return 0;92	}93 94	if (ntable % 2) {95		dev_err(dev, "Pair of temperature vs ADC read value missing\n");96		return -EINVAL;97	}98 99	gti->lookup_table = devm_kcalloc(dev,100					 ntable, sizeof(*gti->lookup_table),101					 GFP_KERNEL);102	if (!gti->lookup_table)103		return -ENOMEM;104 105	ret = of_property_read_u32_array(np, "temperature-lookup-table",106					 (u32 *)gti->lookup_table, ntable);107	if (ret < 0) {108		dev_err(dev, "Failed to read temperature lookup table: %d\n",109			ret);110		return ret;111	}112 113	gti->nlookup_table = ntable / 2;114 115	return 0;116}117 118static int gadc_thermal_probe(struct platform_device *pdev)119{120	struct device *dev = &pdev->dev;121	struct gadc_thermal_info *gti;122	int ret;123 124	if (!dev->of_node) {125		dev_err(dev, "Only DT based supported\n");126		return -ENODEV;127	}128 129	gti = devm_kzalloc(dev, sizeof(*gti), GFP_KERNEL);130	if (!gti)131		return -ENOMEM;132 133	gti->channel = devm_iio_channel_get(dev, "sensor-channel");134	if (IS_ERR(gti->channel))135		return dev_err_probe(dev, PTR_ERR(gti->channel), "IIO channel not found\n");136 137	ret = gadc_thermal_read_linear_lookup_table(dev, gti);138	if (ret < 0)139		return ret;140 141	gti->dev = dev;142 143	gti->tz_dev = devm_thermal_of_zone_register(dev, 0, gti,144						    &gadc_thermal_ops);145	if (IS_ERR(gti->tz_dev)) {146		ret = PTR_ERR(gti->tz_dev);147		if (ret != -EPROBE_DEFER)148			dev_err(dev,149				"Thermal zone sensor register failed: %d\n",150				ret);151		return ret;152	}153 154	devm_thermal_add_hwmon_sysfs(dev, gti->tz_dev);155 156	return 0;157}158 159static const struct of_device_id of_adc_thermal_match[] = {160	{ .compatible = "generic-adc-thermal", },161	{},162};163MODULE_DEVICE_TABLE(of, of_adc_thermal_match);164 165static struct platform_driver gadc_thermal_driver = {166	.driver = {167		.name = "generic-adc-thermal",168		.of_match_table = of_adc_thermal_match,169	},170	.probe = gadc_thermal_probe,171};172 173module_platform_driver(gadc_thermal_driver);174 175MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");176MODULE_DESCRIPTION("Generic ADC thermal driver using IIO framework with DT");177MODULE_LICENSE("GPL v2");178