1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Generic ADC thermal driver 4 * 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/iio/iio.h> 11 #include <linux/kernel.h> 12 #include <linux/module.h> 13 #include <linux/platform_device.h> 14 #include <linux/slab.h> 15 #include <linux/thermal.h> 16 17 #include "thermal_hwmon.h" 18 19 struct gadc_thermal_info { 20 struct device *dev; 21 struct thermal_zone_device *tz_dev; 22 struct iio_channel *channel; 23 s32 *lookup_table; 24 int nlookup_table; 25 }; 26 27 static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val) 28 { 29 int temp, temp_hi, temp_lo, adc_hi, adc_lo; 30 int i; 31 32 if (!gti->lookup_table) 33 return val; 34 35 for (i = 0; i < gti->nlookup_table; i++) { 36 if (val >= gti->lookup_table[2 * i + 1]) 37 break; 38 } 39 40 if (i == 0) { 41 temp = gti->lookup_table[0]; 42 } else if (i >= gti->nlookup_table) { 43 temp = gti->lookup_table[2 * (gti->nlookup_table - 1)]; 44 } else { 45 adc_hi = gti->lookup_table[2 * i - 1]; 46 adc_lo = gti->lookup_table[2 * i + 1]; 47 48 temp_hi = gti->lookup_table[2 * i - 2]; 49 temp_lo = gti->lookup_table[2 * i]; 50 51 temp = temp_hi + mult_frac(temp_lo - temp_hi, val - adc_hi, 52 adc_lo - adc_hi); 53 } 54 55 return temp; 56 } 57 58 static int gadc_thermal_get_temp(struct thermal_zone_device *tz, int *temp) 59 { 60 struct gadc_thermal_info *gti = thermal_zone_device_priv(tz); 61 int val; 62 int ret; 63 64 ret = iio_read_channel_processed(gti->channel, &val); 65 if (ret < 0) 66 return ret; 67 68 *temp = gadc_thermal_adc_to_temp(gti, val); 69 70 return 0; 71 } 72 73 static const struct thermal_zone_device_ops gadc_thermal_ops = { 74 .get_temp = gadc_thermal_get_temp, 75 }; 76 77 static const struct iio_chan_spec gadc_thermal_iio_channels[] = { 78 { 79 .type = IIO_TEMP, 80 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 81 } 82 }; 83 84 static int gadc_thermal_read_raw(struct iio_dev *indio_dev, 85 struct iio_chan_spec const *chan, 86 int *val, int *val2, long mask) 87 { 88 struct gadc_thermal_info *gtinfo = iio_priv(indio_dev); 89 int ret; 90 91 switch (mask) { 92 case IIO_CHAN_INFO_PROCESSED: 93 ret = gadc_thermal_get_temp(gtinfo->tz_dev, val); 94 if (ret) 95 return ret; 96 97 return IIO_VAL_INT; 98 99 default: 100 return -EINVAL; 101 } 102 } 103 104 static const struct iio_info gadc_thermal_iio_info = { 105 .read_raw = gadc_thermal_read_raw, 106 }; 107 108 static int gadc_iio_register(struct device *dev, struct gadc_thermal_info *gti) 109 { 110 struct gadc_thermal_info *gtinfo; 111 struct iio_dev *indio_dev; 112 113 indio_dev = devm_iio_device_alloc(dev, sizeof(*gtinfo)); 114 if (!indio_dev) 115 return -ENOMEM; 116 117 gtinfo = iio_priv(indio_dev); 118 memcpy(gtinfo, gti, sizeof(*gtinfo)); 119 120 indio_dev->name = dev_name(dev); 121 indio_dev->info = &gadc_thermal_iio_info; 122 indio_dev->modes = INDIO_DIRECT_MODE; 123 indio_dev->channels = gadc_thermal_iio_channels; 124 indio_dev->num_channels = ARRAY_SIZE(gadc_thermal_iio_channels); 125 126 return devm_iio_device_register(dev, indio_dev); 127 } 128 129 static int gadc_thermal_read_linear_lookup_table(struct device *dev, 130 struct gadc_thermal_info *gti) 131 { 132 struct device_node *np = dev->of_node; 133 enum iio_chan_type chan_type; 134 int ntable; 135 int ret; 136 137 ntable = of_property_count_elems_of_size(np, "temperature-lookup-table", 138 sizeof(u32)); 139 if (ntable <= 0) { 140 ret = iio_get_channel_type(gti->channel, &chan_type); 141 if (ret || chan_type != IIO_TEMP) 142 dev_notice(dev, 143 "no lookup table, assuming DAC channel returns milliCelcius\n"); 144 return 0; 145 } 146 147 if (ntable % 2) { 148 dev_err(dev, "Pair of temperature vs ADC read value missing\n"); 149 return -EINVAL; 150 } 151 152 gti->lookup_table = devm_kcalloc(dev, 153 ntable, sizeof(*gti->lookup_table), 154 GFP_KERNEL); 155 if (!gti->lookup_table) 156 return -ENOMEM; 157 158 ret = of_property_read_u32_array(np, "temperature-lookup-table", 159 (u32 *)gti->lookup_table, ntable); 160 if (ret < 0) { 161 dev_err(dev, "Failed to read temperature lookup table: %d\n", 162 ret); 163 return ret; 164 } 165 166 gti->nlookup_table = ntable / 2; 167 168 return 0; 169 } 170 171 static int gadc_thermal_probe(struct platform_device *pdev) 172 { 173 struct device *dev = &pdev->dev; 174 struct gadc_thermal_info *gti; 175 int ret; 176 177 if (!dev->of_node) { 178 dev_err(dev, "Only DT based supported\n"); 179 return -ENODEV; 180 } 181 182 gti = devm_kzalloc(dev, sizeof(*gti), GFP_KERNEL); 183 if (!gti) 184 return -ENOMEM; 185 186 gti->channel = devm_iio_channel_get(dev, "sensor-channel"); 187 if (IS_ERR(gti->channel)) 188 return dev_err_probe(dev, PTR_ERR(gti->channel), "IIO channel not found\n"); 189 190 ret = gadc_thermal_read_linear_lookup_table(dev, gti); 191 if (ret < 0) 192 return ret; 193 194 gti->dev = dev; 195 196 gti->tz_dev = devm_thermal_of_zone_register(dev, 0, gti, 197 &gadc_thermal_ops); 198 if (IS_ERR(gti->tz_dev)) { 199 ret = PTR_ERR(gti->tz_dev); 200 if (ret != -EPROBE_DEFER) 201 dev_err(dev, 202 "Thermal zone sensor register failed: %d\n", 203 ret); 204 return ret; 205 } 206 207 devm_thermal_add_hwmon_sysfs(dev, gti->tz_dev); 208 209 return gadc_iio_register(&pdev->dev, gti); 210 } 211 212 static const struct of_device_id of_adc_thermal_match[] = { 213 { .compatible = "generic-adc-thermal", }, 214 {}, 215 }; 216 MODULE_DEVICE_TABLE(of, of_adc_thermal_match); 217 218 static struct platform_driver gadc_thermal_driver = { 219 .driver = { 220 .name = "generic-adc-thermal", 221 .of_match_table = of_adc_thermal_match, 222 }, 223 .probe = gadc_thermal_probe, 224 }; 225 226 module_platform_driver(gadc_thermal_driver); 227 228 MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); 229 MODULE_DESCRIPTION("Generic ADC thermal driver using IIO framework with DT"); 230 MODULE_LICENSE("GPL v2"); 231