File Index
Files and directories
Sign in
FILE

adt7x10.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/adt7x10.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * adt7x10.c - Part of lm_sensors, Linux kernel modules for hardware
  4 *	 monitoring
  5 * This driver handles the ADT7410 and compatible digital temperature sensors.
  6 * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
  7 * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
  8 * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
  9 */
 10
 11#include <linux/device.h>
 12#include <linux/module.h>
 13#include <linux/init.h>
 14#include <linux/slab.h>
 15#include <linux/jiffies.h>
 16#include <linux/hwmon.h>
 17#include <linux/err.h>
 18#include <linux/delay.h>
 19#include <linux/interrupt.h>
 20#include <linux/regmap.h>
 21
 22#include "adt7x10.h"
 23
 24/*
 25 * ADT7X10 status
 26 */
 27#define ADT7X10_STAT_T_LOW		(1 << 4)
 28#define ADT7X10_STAT_T_HIGH		(1 << 5)
 29#define ADT7X10_STAT_T_CRIT		(1 << 6)
 30#define ADT7X10_STAT_NOT_RDY		(1 << 7)
 31
 32/*
 33 * ADT7X10 config
 34 */
 35#define ADT7X10_FAULT_QUEUE_MASK	(1 << 0 | 1 << 1)
 36#define ADT7X10_CT_POLARITY		(1 << 2)
 37#define ADT7X10_INT_POLARITY		(1 << 3)
 38#define ADT7X10_EVENT_MODE		(1 << 4)
 39#define ADT7X10_MODE_MASK		(1 << 5 | 1 << 6)
 40#define ADT7X10_FULL			(0 << 5 | 0 << 6)
 41#define ADT7X10_PD			(1 << 5 | 1 << 6)
 42#define ADT7X10_RESOLUTION		(1 << 7)
 43
 44/*
 45 * ADT7X10 masks
 46 */
 47#define ADT7X10_T13_VALUE_MASK		0xFFF8
 48#define ADT7X10_T_HYST_MASK		0xF
 49
 50/* straight from the datasheet */
 51#define ADT7X10_TEMP_MIN (-55000)
 52#define ADT7X10_TEMP_MAX 150000
 53
 54/* Each client has this additional data */
 55struct adt7x10_data {
 56	struct regmap		*regmap;
 57	u8			config;
 58	u8			oldconfig;
 59	bool			valid;		/* true if temperature valid */
 60};
 61
 62enum {
 63	adt7x10_temperature = 0,
 64	adt7x10_t_alarm_high,
 65	adt7x10_t_alarm_low,
 66	adt7x10_t_crit,
 67};
 68
 69static const u8 ADT7X10_REG_TEMP[] = {
 70	[adt7x10_temperature] = ADT7X10_TEMPERATURE,		/* input */
 71	[adt7x10_t_alarm_high] = ADT7X10_T_ALARM_HIGH,		/* high */
 72	[adt7x10_t_alarm_low] = ADT7X10_T_ALARM_LOW,		/* low */
 73	[adt7x10_t_crit] = ADT7X10_T_CRIT,			/* critical */
 74};
 75
 76static irqreturn_t adt7x10_irq_handler(int irq, void *private)
 77{
 78	struct device *dev = private;
 79	struct adt7x10_data *d = dev_get_drvdata(dev);
 80	unsigned int status;
 81	int ret;
 82
 83	ret = regmap_read(d->regmap, ADT7X10_STATUS, &status);
 84	if (ret < 0)
 85		return IRQ_HANDLED;
 86
 87	if (status & ADT7X10_STAT_T_HIGH)
 88		hwmon_notify_event(dev, hwmon_temp, hwmon_temp_max_alarm, 0);
 89	if (status & ADT7X10_STAT_T_LOW)
 90		hwmon_notify_event(dev, hwmon_temp, hwmon_temp_min_alarm, 0);
 91	if (status & ADT7X10_STAT_T_CRIT)
 92		hwmon_notify_event(dev, hwmon_temp, hwmon_temp_crit_alarm, 0);
 93
 94	return IRQ_HANDLED;
 95}
 96
 97static int adt7x10_temp_ready(struct regmap *regmap)
 98{
 99	unsigned int status;
100	int i, ret;
101
102	for (i = 0; i < 6; i++) {
103		ret = regmap_read(regmap, ADT7X10_STATUS, &status);
104		if (ret < 0)
105			return ret;
106		if (!(status & ADT7X10_STAT_NOT_RDY))
107			return 0;
108		msleep(60);
109	}
110	return -ETIMEDOUT;
111}
112
113static s16 ADT7X10_TEMP_TO_REG(long temp)
114{
115	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7X10_TEMP_MIN,
116					   ADT7X10_TEMP_MAX) * 128, 1000);
117}
118
119static int ADT7X10_REG_TO_TEMP(struct adt7x10_data *data, s16 reg)
120{
121	/* in 13 bit mode, bits 0-2 are status flags - mask them out */
122	if (!(data->config & ADT7X10_RESOLUTION))
123		reg &= ADT7X10_T13_VALUE_MASK;
124	/*
125	 * temperature is stored in twos complement format, in steps of
126	 * 1/128°C
127	 */
128	return DIV_ROUND_CLOSEST(reg * 1000, 128);
129}
130
131/*-----------------------------------------------------------------------*/
132
133static int adt7x10_temp_read(struct adt7x10_data *data, int index, long *val)
134{
135	unsigned int regval;
136	int ret;
137
138	if (index == adt7x10_temperature && !data->valid) {
139		/* wait for valid temperature */
140		ret = adt7x10_temp_ready(data->regmap);
141		if (ret)
142			return ret;
143		data->valid = true;
144	}
145
146	ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], &regval);
147	if (ret)
148		return ret;
149
150	*val = ADT7X10_REG_TO_TEMP(data, regval);
151	return 0;
152}
153
154static int adt7x10_temp_write(struct adt7x10_data *data, int index, long temp)
155{
156	return regmap_write(data->regmap, ADT7X10_REG_TEMP[index],
157			    ADT7X10_TEMP_TO_REG(temp));
158}
159
160static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val)
161{
162	unsigned int regs[2] = {ADT7X10_T_HYST, ADT7X10_REG_TEMP[index]};
163	int hyst, ret;
164	u16 regdata[2];
165
166	ret = regmap_multi_reg_read(data->regmap, regs, regdata, 2);
167	if (ret)
168		return ret;
169
170	hyst = (regdata[0] & ADT7X10_T_HYST_MASK) * 1000;
171
172	/*
173	 * hysteresis is stored as a 4 bit offset in the device, convert it
174	 * to an absolute value
175	 */
176	/* min has positive offset, others have negative */
177	if (index == adt7x10_t_alarm_low)
178		hyst = -hyst;
179
180	*val = ADT7X10_REG_TO_TEMP(data, regdata[1]) - hyst;
181	return 0;
182}
183
184static int adt7x10_hyst_write(struct adt7x10_data *data, long hyst)
185{
186	unsigned int regval;
187	int limit, ret;
188
189	/* convert absolute hysteresis value to a 4 bit delta value */
190	ret = regmap_read(data->regmap, ADT7X10_T_ALARM_HIGH, &regval);
191	if (ret < 0)
192		return ret;
193
194	limit = ADT7X10_REG_TO_TEMP(data, regval);
195
196	hyst = clamp_val(hyst, ADT7X10_TEMP_MIN, ADT7X10_TEMP_MAX);
197	regval = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0,
198			   ADT7X10_T_HYST_MASK);
199	return regmap_write(data->regmap, ADT7X10_T_HYST, regval);
200}
201
202static int adt7x10_alarm_read(struct adt7x10_data *data, int index, long *val)
203{
204	unsigned int status;
205	int ret;
206
207	ret = regmap_read(data->regmap, ADT7X10_STATUS, &status);
208	if (ret < 0)
209		return ret;
210
211	*val = !!(status & index);
212
213	return 0;
214}
215
216static umode_t adt7x10_is_visible(const void *data,
217				  enum hwmon_sensor_types type,
218				  u32 attr, int channel)
219{
220	switch (attr) {
221	case hwmon_temp_max:
222	case hwmon_temp_min:
223	case hwmon_temp_crit:
224	case hwmon_temp_max_hyst:
225		return 0644;
226	case hwmon_temp_input:
227	case hwmon_temp_min_alarm:
228	case hwmon_temp_max_alarm:
229	case hwmon_temp_crit_alarm:
230	case hwmon_temp_min_hyst:
231	case hwmon_temp_crit_hyst:
232		return 0444;
233	default:
234		break;
235	}
236
237	return 0;
238}
239
240static int adt7x10_read(struct device *dev, enum hwmon_sensor_types type,
241			u32 attr, int channel, long *val)
242{
243	struct adt7x10_data *data = dev_get_drvdata(dev);
244
245	switch (attr) {
246	case hwmon_temp_input:
247		return adt7x10_temp_read(data, adt7x10_temperature, val);
248	case hwmon_temp_max:
249		return adt7x10_temp_read(data, adt7x10_t_alarm_high, val);
250	case hwmon_temp_min:
251		return adt7x10_temp_read(data, adt7x10_t_alarm_low, val);
252	case hwmon_temp_crit:
253		return adt7x10_temp_read(data, adt7x10_t_crit, val);
254	case hwmon_temp_max_hyst:
255		return adt7x10_hyst_read(data, adt7x10_t_alarm_high, val);
256	case hwmon_temp_min_hyst:
257		return adt7x10_hyst_read(data, adt7x10_t_alarm_low, val);
258	case hwmon_temp_crit_hyst:
259		return adt7x10_hyst_read(data, adt7x10_t_crit, val);
260	case hwmon_temp_min_alarm:
261		return adt7x10_alarm_read(data, ADT7X10_STAT_T_LOW, val);
262	case hwmon_temp_max_alarm:
263		return adt7x10_alarm_read(data, ADT7X10_STAT_T_HIGH, val);
264	case hwmon_temp_crit_alarm:
265		return adt7x10_alarm_read(data, ADT7X10_STAT_T_CRIT, val);
266	default:
267		return -EOPNOTSUPP;
268	}
269}
270
271static int adt7x10_write(struct device *dev, enum hwmon_sensor_types type,
272			 u32 attr, int channel, long val)
273{
274	struct adt7x10_data *data = dev_get_drvdata(dev);
275
276	switch (attr) {
277	case hwmon_temp_max:
278		return adt7x10_temp_write(data, adt7x10_t_alarm_high, val);
279	case hwmon_temp_min:
280		return adt7x10_temp_write(data, adt7x10_t_alarm_low, val);
281	case hwmon_temp_crit:
282		return adt7x10_temp_write(data, adt7x10_t_crit, val);
283	case hwmon_temp_max_hyst:
284		return adt7x10_hyst_write(data, val);
285	default:
286		return -EOPNOTSUPP;
287	}
288}
289
290static const struct hwmon_channel_info * const adt7x10_info[] = {
291	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
292			   HWMON_T_CRIT | HWMON_T_MAX_HYST | HWMON_T_MIN_HYST |
293			   HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
294			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM),
295	NULL,
296};
297
298static const struct hwmon_ops adt7x10_hwmon_ops = {
299	.is_visible = adt7x10_is_visible,
300	.read = adt7x10_read,
301	.write = adt7x10_write,
302};
303
304static const struct hwmon_chip_info adt7x10_chip_info = {
305	.ops = &adt7x10_hwmon_ops,
306	.info = adt7x10_info,
307};
308
309static void adt7x10_restore_config(void *private)
310{
311	struct adt7x10_data *data = private;
312
313	regmap_write(data->regmap, ADT7X10_CONFIG, data->oldconfig);
314}
315
316int adt7x10_probe(struct device *dev, const char *name, int irq,
317		  struct regmap *regmap)
318{
319	struct adt7x10_data *data;
320	unsigned int config;
321	struct device *hdev;
322	int ret;
323
324	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
325	if (!data)
326		return -ENOMEM;
327
328	data->regmap = regmap;
329
330	dev_set_drvdata(dev, data);
331
332	/* configure as specified */
333	ret = regmap_read(regmap, ADT7X10_CONFIG, &config);
334	if (ret < 0) {
335		dev_dbg(dev, "Can't read config? %d\n", ret);
336		return ret;
337	}
338	data->oldconfig = config;
339
340	/*
341	 * Set to 16 bit resolution, continous conversion and comparator mode.
342	 */
343	data->config = data->oldconfig;
344	data->config &= ~(ADT7X10_MODE_MASK | ADT7X10_CT_POLARITY |
345			ADT7X10_INT_POLARITY);
346	data->config |= ADT7X10_FULL | ADT7X10_RESOLUTION | ADT7X10_EVENT_MODE;
347
348	if (data->config != data->oldconfig) {
349		ret = regmap_write(regmap, ADT7X10_CONFIG, data->config);
350		if (ret)
351			return ret;
352		ret = devm_add_action_or_reset(dev, adt7x10_restore_config, data);
353		if (ret)
354			return ret;
355	}
356	dev_dbg(dev, "Config %02x\n", data->config);
357
358	hdev = devm_hwmon_device_register_with_info(dev, name, data,
359						    &adt7x10_chip_info, NULL);
360	if (IS_ERR(hdev))
361		return PTR_ERR(hdev);
362
363	if (irq > 0) {
364		ret = devm_request_threaded_irq(dev, irq, NULL,
365						adt7x10_irq_handler,
366						IRQF_TRIGGER_FALLING |
367						IRQF_ONESHOT,
368						dev_name(dev), hdev);
369		if (ret)
370			return ret;
371	}
372
373	return 0;
374}
375EXPORT_SYMBOL_GPL(adt7x10_probe);
376
377static int adt7x10_suspend(struct device *dev)
378{
379	struct adt7x10_data *data = dev_get_drvdata(dev);
380
381	return regmap_write(data->regmap, ADT7X10_CONFIG,
382			    data->config | ADT7X10_PD);
383}
384
385static int adt7x10_resume(struct device *dev)
386{
387	struct adt7x10_data *data = dev_get_drvdata(dev);
388
389	return regmap_write(data->regmap, ADT7X10_CONFIG, data->config);
390}
391
392EXPORT_SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume);
393
394MODULE_AUTHOR("Hartmut Knaack");
395MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code");
396MODULE_LICENSE("GPL");
~/ Read-only file preview.