File Index
Files and directories
Sign in
FILE

mcp9600.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/temperature/mcp9600.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * mcp9600.c - Support for Microchip MCP9600 thermocouple EMF converter
  4 *
  5 * Copyright (c) 2022 Andrew Hepp
  6 * Author: <andrew.hepp@ahepp.dev>
  7 */
  8
  9#include <linux/bitfield.h>
 10#include <linux/bitops.h>
 11#include <linux/bits.h>
 12#include <linux/err.h>
 13#include <linux/i2c.h>
 14#include <linux/init.h>
 15#include <linux/interrupt.h>
 16#include <linux/irq.h>
 17#include <linux/math.h>
 18#include <linux/minmax.h>
 19#include <linux/mod_devicetable.h>
 20#include <linux/module.h>
 21
 22#include <linux/iio/events.h>
 23#include <linux/iio/iio.h>
 24
 25#include <dt-bindings/iio/temperature/thermocouple.h>
 26
 27/* MCP9600 registers */
 28#define MCP9600_HOT_JUNCTION		0x00
 29#define MCP9600_COLD_JUNCTION		0x02
 30#define MCP9600_STATUS			0x04
 31#define MCP9600_STATUS_ALERT(x)		BIT(x)
 32#define MCP9600_SENSOR_CFG		0x05
 33#define MCP9600_SENSOR_TYPE_MASK	GENMASK(6, 4)
 34#define MCP9600_ALERT_CFG1		0x08
 35#define MCP9600_ALERT_CFG(x)		(MCP9600_ALERT_CFG1 + (x - 1))
 36#define MCP9600_ALERT_CFG_ENABLE	BIT(0)
 37#define MCP9600_ALERT_CFG_ACTIVE_HIGH	BIT(2)
 38#define MCP9600_ALERT_CFG_FALLING	BIT(3)
 39#define MCP9600_ALERT_CFG_COLD_JUNCTION	BIT(4)
 40#define MCP9600_ALERT_HYSTERESIS1	0x0c
 41#define MCP9600_ALERT_HYSTERESIS(x)	(MCP9600_ALERT_HYSTERESIS1 + (x - 1))
 42#define MCP9600_ALERT_LIMIT1		0x10
 43#define MCP9600_ALERT_LIMIT(x)		(MCP9600_ALERT_LIMIT1 + (x - 1))
 44#define MCP9600_ALERT_LIMIT_MASK	GENMASK(15, 2)
 45#define MCP9600_DEVICE_ID		0x20
 46
 47/* MCP9600 device id value */
 48#define MCP9600_DEVICE_ID_MCP9600	0x40
 49#define MCP9600_DEVICE_ID_MCP9601	0x41
 50
 51#define MCP9600_ALERT_COUNT		4
 52
 53#define MCP9600_MIN_TEMP_HOT_JUNCTION_MICRO	-200000000
 54#define MCP9600_MAX_TEMP_HOT_JUNCTION_MICRO	1800000000
 55
 56#define MCP9600_MIN_TEMP_COLD_JUNCTION_MICRO	-40000000
 57#define MCP9600_MAX_TEMP_COLD_JUNCTION_MICRO	125000000
 58
 59enum mcp9600_alert {
 60	MCP9600_ALERT1,
 61	MCP9600_ALERT2,
 62	MCP9600_ALERT3,
 63	MCP9600_ALERT4
 64};
 65
 66static const char * const mcp9600_alert_name[MCP9600_ALERT_COUNT] = {
 67	[MCP9600_ALERT1] = "alert1",
 68	[MCP9600_ALERT2] = "alert2",
 69	[MCP9600_ALERT3] = "alert3",
 70	[MCP9600_ALERT4] = "alert4",
 71};
 72
 73/* Map between dt-bindings enum and the chip's type value */
 74static const unsigned int mcp9600_type_map[] = {
 75	[THERMOCOUPLE_TYPE_K] = 0,
 76	[THERMOCOUPLE_TYPE_J] = 1,
 77	[THERMOCOUPLE_TYPE_T] = 2,
 78	[THERMOCOUPLE_TYPE_N] = 3,
 79	[THERMOCOUPLE_TYPE_S] = 4,
 80	[THERMOCOUPLE_TYPE_E] = 5,
 81	[THERMOCOUPLE_TYPE_B] = 6,
 82	[THERMOCOUPLE_TYPE_R] = 7,
 83};
 84
 85/* Map thermocouple type to a char for iio info in sysfs */
 86static const int mcp9600_tc_types[] = {
 87	[THERMOCOUPLE_TYPE_K] = 'K',
 88	[THERMOCOUPLE_TYPE_J] = 'J',
 89	[THERMOCOUPLE_TYPE_T] = 'T',
 90	[THERMOCOUPLE_TYPE_N] = 'N',
 91	[THERMOCOUPLE_TYPE_S] = 'S',
 92	[THERMOCOUPLE_TYPE_E] = 'E',
 93	[THERMOCOUPLE_TYPE_B] = 'B',
 94	[THERMOCOUPLE_TYPE_R] = 'R',
 95};
 96
 97static const struct iio_event_spec mcp9600_events[] = {
 98	{
 99		.type = IIO_EV_TYPE_THRESH,
100		.dir = IIO_EV_DIR_RISING,
101		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
102				 BIT(IIO_EV_INFO_VALUE) |
103				 BIT(IIO_EV_INFO_HYSTERESIS),
104	},
105	{
106		.type = IIO_EV_TYPE_THRESH,
107		.dir = IIO_EV_DIR_FALLING,
108		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
109				 BIT(IIO_EV_INFO_VALUE) |
110				 BIT(IIO_EV_INFO_HYSTERESIS),
111	},
112};
113
114struct mcp_chip_info {
115	u8 chip_id;
116	const char *chip_name;
117};
118
119struct mcp9600_data {
120	struct i2c_client *client;
121	u32 thermocouple_type;
122};
123
124static int mcp9600_config(struct mcp9600_data *data)
125{
126	struct i2c_client *client = data->client;
127	int ret;
128	u8 cfg;
129
130	cfg  = FIELD_PREP(MCP9600_SENSOR_TYPE_MASK,
131			  mcp9600_type_map[data->thermocouple_type]);
132
133	ret = i2c_smbus_write_byte_data(client, MCP9600_SENSOR_CFG, cfg);
134	if (ret < 0) {
135		dev_err(&client->dev, "Failed to set sensor configuration\n");
136		return ret;
137	}
138
139	return 0;
140}
141
142#define MCP9600_CHANNELS(hj_num_ev, hj_ev_spec_off, cj_num_ev, cj_ev_spec_off) \
143	{								       \
144		{							       \
145			.type = IIO_TEMP,				       \
146			.address = MCP9600_HOT_JUNCTION,		       \
147			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	       \
148					      BIT(IIO_CHAN_INFO_THERMOCOUPLE_TYPE) | \
149					      BIT(IIO_CHAN_INFO_SCALE),	       \
150			.event_spec = &mcp9600_events[hj_ev_spec_off],	       \
151			.num_event_specs = hj_num_ev,			       \
152		},							       \
153		{							       \
154			.type = IIO_TEMP,				       \
155			.address = MCP9600_COLD_JUNCTION,		       \
156			.channel2 = IIO_MOD_TEMP_AMBIENT,		       \
157			.modified = 1,					       \
158			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	       \
159					      BIT(IIO_CHAN_INFO_SCALE),	       \
160			.event_spec = &mcp9600_events[cj_ev_spec_off],	       \
161			.num_event_specs = cj_num_ev,			       \
162		},							       \
163	}
164
165static const struct iio_chan_spec mcp9600_channels[][2] = {
166	MCP9600_CHANNELS(0, 0, 0, 0), /* Alerts: - - - - */
167	MCP9600_CHANNELS(1, 0, 0, 0), /* Alerts: 1 - - - */
168	MCP9600_CHANNELS(1, 1, 0, 0), /* Alerts: - 2 - - */
169	MCP9600_CHANNELS(2, 0, 0, 0), /* Alerts: 1 2 - - */
170	MCP9600_CHANNELS(0, 0, 1, 0), /* Alerts: - - 3 - */
171	MCP9600_CHANNELS(1, 0, 1, 0), /* Alerts: 1 - 3 - */
172	MCP9600_CHANNELS(1, 1, 1, 0), /* Alerts: - 2 3 - */
173	MCP9600_CHANNELS(2, 0, 1, 0), /* Alerts: 1 2 3 - */
174	MCP9600_CHANNELS(0, 0, 1, 1), /* Alerts: - - - 4 */
175	MCP9600_CHANNELS(1, 0, 1, 1), /* Alerts: 1 - - 4 */
176	MCP9600_CHANNELS(1, 1, 1, 1), /* Alerts: - 2 - 4 */
177	MCP9600_CHANNELS(2, 0, 1, 1), /* Alerts: 1 2 - 4 */
178	MCP9600_CHANNELS(0, 0, 2, 0), /* Alerts: - - 3 4 */
179	MCP9600_CHANNELS(1, 0, 2, 0), /* Alerts: 1 - 3 4 */
180	MCP9600_CHANNELS(1, 1, 2, 0), /* Alerts: - 2 3 4 */
181	MCP9600_CHANNELS(2, 0, 2, 0), /* Alerts: 1 2 3 4 */
182};
183
184static int mcp9600_read(struct mcp9600_data *data,
185			struct iio_chan_spec const *chan, int *val)
186{
187	int ret;
188
189	ret = i2c_smbus_read_word_swapped(data->client, chan->address);
190
191	if (ret < 0)
192		return ret;
193
194	*val = sign_extend32(ret, 15);
195
196	return 0;
197}
198
199static int mcp9600_read_raw(struct iio_dev *indio_dev,
200			    struct iio_chan_spec const *chan, int *val,
201			    int *val2, long mask)
202{
203	struct mcp9600_data *data = iio_priv(indio_dev);
204	int ret;
205
206	switch (mask) {
207	case IIO_CHAN_INFO_RAW:
208		ret = mcp9600_read(data, chan, val);
209		if (ret)
210			return ret;
211		return IIO_VAL_INT;
212	case IIO_CHAN_INFO_SCALE:
213		*val = 62;
214		*val2 = 500000;
215		return IIO_VAL_INT_PLUS_MICRO;
216	case IIO_CHAN_INFO_THERMOCOUPLE_TYPE:
217		*val = mcp9600_tc_types[data->thermocouple_type];
218		return IIO_VAL_CHAR;
219	default:
220		return -EINVAL;
221	}
222}
223
224static int mcp9600_get_alert_index(int channel2, enum iio_event_direction dir)
225{
226	if (channel2 == IIO_MOD_TEMP_AMBIENT) {
227		if (dir == IIO_EV_DIR_RISING)
228			return MCP9600_ALERT3;
229		else
230			return MCP9600_ALERT4;
231	} else {
232		if (dir == IIO_EV_DIR_RISING)
233			return MCP9600_ALERT1;
234		else
235			return MCP9600_ALERT2;
236	}
237}
238
239static int mcp9600_read_event_config(struct iio_dev *indio_dev,
240				     const struct iio_chan_spec *chan,
241				     enum iio_event_type type,
242				     enum iio_event_direction dir)
243{
244	struct mcp9600_data *data = iio_priv(indio_dev);
245	struct i2c_client *client = data->client;
246	int i, ret;
247
248	i = mcp9600_get_alert_index(chan->channel2, dir);
249	ret = i2c_smbus_read_byte_data(client, MCP9600_ALERT_CFG(i + 1));
250	if (ret < 0)
251		return ret;
252
253	return FIELD_GET(MCP9600_ALERT_CFG_ENABLE, ret);
254}
255
256static int mcp9600_write_event_config(struct iio_dev *indio_dev,
257				      const struct iio_chan_spec *chan,
258				      enum iio_event_type type,
259				      enum iio_event_direction dir,
260				      bool state)
261{
262	struct mcp9600_data *data = iio_priv(indio_dev);
263	struct i2c_client *client = data->client;
264	int i, ret;
265
266	i = mcp9600_get_alert_index(chan->channel2, dir);
267	ret = i2c_smbus_read_byte_data(client, MCP9600_ALERT_CFG(i + 1));
268	if (ret < 0)
269		return ret;
270
271	if (state)
272		ret |= MCP9600_ALERT_CFG_ENABLE;
273	else
274		ret &= ~MCP9600_ALERT_CFG_ENABLE;
275
276	return i2c_smbus_write_byte_data(client, MCP9600_ALERT_CFG(i + 1), ret);
277}
278
279static int mcp9600_read_thresh(struct iio_dev *indio_dev,
280			       const struct iio_chan_spec *chan,
281			       enum iio_event_type type,
282			       enum iio_event_direction dir,
283			       enum iio_event_info info, int *val, int *val2)
284{
285	struct mcp9600_data *data = iio_priv(indio_dev);
286	struct i2c_client *client = data->client;
287	s32 ret;
288	int i;
289
290	i = mcp9600_get_alert_index(chan->channel2, dir);
291	switch (info) {
292	case IIO_EV_INFO_VALUE:
293		ret = i2c_smbus_read_word_swapped(client, MCP9600_ALERT_LIMIT(i + 1));
294		if (ret < 0)
295			return ret;
296		/*
297		 * Temperature is stored in two’s complement format in
298		 * bits(15:2), LSB is 0.25 degree celsius.
299		 */
300		*val = sign_extend32(FIELD_GET(MCP9600_ALERT_LIMIT_MASK, ret), 13);
301		*val2 = 4;
302		return IIO_VAL_FRACTIONAL;
303	case IIO_EV_INFO_HYSTERESIS:
304		ret = i2c_smbus_read_byte_data(client, MCP9600_ALERT_HYSTERESIS(i + 1));
305		if (ret < 0)
306			return ret;
307
308		*val = ret;
309		return IIO_VAL_INT;
310	default:
311		return -EINVAL;
312	}
313}
314
315static int mcp9600_write_thresh(struct iio_dev *indio_dev,
316				const struct iio_chan_spec *chan,
317				enum iio_event_type type,
318				enum iio_event_direction dir,
319				enum iio_event_info info, int val, int val2)
320{
321	struct mcp9600_data *data = iio_priv(indio_dev);
322	struct i2c_client *client = data->client;
323	int s_val, i;
324	s16 thresh;
325	u8 hyst;
326
327	i = mcp9600_get_alert_index(chan->channel2, dir);
328	switch (info) {
329	case IIO_EV_INFO_VALUE:
330		/* Scale value to include decimal part into calculations */
331		s_val = (val < 0) ? ((val * 1000000) - val2) :
332				    ((val * 1000000) + val2);
333		if (chan->channel2 == IIO_MOD_TEMP_AMBIENT) {
334			s_val = max(s_val, MCP9600_MIN_TEMP_COLD_JUNCTION_MICRO);
335			s_val = min(s_val, MCP9600_MAX_TEMP_COLD_JUNCTION_MICRO);
336		} else {
337			s_val = max(s_val, MCP9600_MIN_TEMP_HOT_JUNCTION_MICRO);
338			s_val = min(s_val, MCP9600_MAX_TEMP_HOT_JUNCTION_MICRO);
339		}
340
341		/*
342		 * Shift length 4 bits = 2(15:2) + 2(0.25 LSB), temperature is
343		 * stored in two’s complement format.
344		 */
345		thresh = (s16)(s_val / (1000000 >> 4));
346		return i2c_smbus_write_word_swapped(client,
347						    MCP9600_ALERT_LIMIT(i + 1),
348						    thresh);
349	case IIO_EV_INFO_HYSTERESIS:
350		hyst = min(abs(val), 255);
351		return i2c_smbus_write_byte_data(client,
352						 MCP9600_ALERT_HYSTERESIS(i + 1),
353						 hyst);
354	default:
355		return -EINVAL;
356	}
357}
358
359static const struct iio_info mcp9600_info = {
360	.read_raw = mcp9600_read_raw,
361	.read_event_config = mcp9600_read_event_config,
362	.write_event_config = mcp9600_write_event_config,
363	.read_event_value = mcp9600_read_thresh,
364	.write_event_value = mcp9600_write_thresh,
365};
366
367static irqreturn_t mcp9600_alert_handler(void *private,
368					 enum mcp9600_alert alert,
369					 enum iio_modifier mod,
370					 enum iio_event_direction dir)
371{
372	struct iio_dev *indio_dev = private;
373	struct mcp9600_data *data = iio_priv(indio_dev);
374	int ret;
375
376	ret = i2c_smbus_read_byte_data(data->client, MCP9600_STATUS);
377	if (ret < 0)
378		return IRQ_HANDLED;
379
380	if (!(ret & MCP9600_STATUS_ALERT(alert)))
381		return IRQ_NONE;
382
383	iio_push_event(indio_dev,
384		       IIO_MOD_EVENT_CODE(IIO_TEMP, 0, mod, IIO_EV_TYPE_THRESH,
385					  dir),
386		       iio_get_time_ns(indio_dev));
387
388	return IRQ_HANDLED;
389}
390
391static irqreturn_t mcp9600_alert1_handler(int irq, void *private)
392{
393	return mcp9600_alert_handler(private, MCP9600_ALERT1, IIO_NO_MOD,
394				     IIO_EV_DIR_RISING);
395}
396
397static irqreturn_t mcp9600_alert2_handler(int irq, void *private)
398{
399	return mcp9600_alert_handler(private, MCP9600_ALERT2, IIO_NO_MOD,
400				     IIO_EV_DIR_FALLING);
401}
402
403static irqreturn_t mcp9600_alert3_handler(int irq, void *private)
404{
405	return mcp9600_alert_handler(private, MCP9600_ALERT3,
406				     IIO_MOD_TEMP_AMBIENT, IIO_EV_DIR_RISING);
407}
408
409static irqreturn_t mcp9600_alert4_handler(int irq, void *private)
410{
411	return mcp9600_alert_handler(private, MCP9600_ALERT4,
412				     IIO_MOD_TEMP_AMBIENT, IIO_EV_DIR_FALLING);
413}
414
415static irqreturn_t (*mcp9600_alert_handler_func[MCP9600_ALERT_COUNT]) (int, void *) = {
416	mcp9600_alert1_handler,
417	mcp9600_alert2_handler,
418	mcp9600_alert3_handler,
419	mcp9600_alert4_handler,
420};
421
422static int mcp9600_probe_alerts(struct iio_dev *indio_dev)
423{
424	struct mcp9600_data *data = iio_priv(indio_dev);
425	struct i2c_client *client = data->client;
426	struct device *dev = &client->dev;
427	struct fwnode_handle *fwnode = dev_fwnode(dev);
428	unsigned int irq_type;
429	int ret, irq, i;
430	u8 val, ch_sel;
431
432	/*
433	 * alert1: hot junction, rising temperature
434	 * alert2: hot junction, falling temperature
435	 * alert3: cold junction, rising temperature
436	 * alert4: cold junction, falling temperature
437	 */
438	ch_sel = 0;
439	for (i = 0; i < MCP9600_ALERT_COUNT; i++) {
440		irq = fwnode_irq_get_byname(fwnode, mcp9600_alert_name[i]);
441		if (irq <= 0)
442			continue;
443
444		val = 0;
445		irq_type = irq_get_trigger_type(irq);
446		if (irq_type == IRQ_TYPE_EDGE_RISING)
447			val |= MCP9600_ALERT_CFG_ACTIVE_HIGH;
448
449		if (i == MCP9600_ALERT2 || i == MCP9600_ALERT4)
450			val |= MCP9600_ALERT_CFG_FALLING;
451
452		if (i == MCP9600_ALERT3 || i == MCP9600_ALERT4)
453			val |= MCP9600_ALERT_CFG_COLD_JUNCTION;
454
455		ret = i2c_smbus_write_byte_data(client,
456						MCP9600_ALERT_CFG(i + 1),
457						val);
458		if (ret < 0)
459			return ret;
460
461		ret = devm_request_threaded_irq(dev, irq, NULL,
462						mcp9600_alert_handler_func[i],
463						IRQF_ONESHOT, "mcp9600",
464						indio_dev);
465		if (ret)
466			return ret;
467
468		ch_sel |= BIT(i);
469	}
470
471	return ch_sel;
472}
473
474static int mcp9600_probe(struct i2c_client *client)
475{
476	struct device *dev = &client->dev;
477	const struct mcp_chip_info *chip_info;
478	struct iio_dev *indio_dev;
479	struct mcp9600_data *data;
480	int ch_sel, dev_id, ret;
481
482	chip_info = i2c_get_match_data(client);
483	if (!chip_info)
484		return dev_err_probe(dev, -ENODEV,
485				     "No chip-info found for device\n");
486
487	dev_id = i2c_smbus_read_byte_data(client, MCP9600_DEVICE_ID);
488	if (dev_id < 0)
489		return dev_err_probe(dev, dev_id, "Failed to read device ID\n");
490
491	switch (dev_id) {
492	case MCP9600_DEVICE_ID_MCP9600:
493	case MCP9600_DEVICE_ID_MCP9601:
494		if (dev_id != chip_info->chip_id)
495			dev_warn(dev,
496				 "Expected id %02x, but device responded with %02x\n",
497				 chip_info->chip_id, dev_id);
498		break;
499
500	default:
501		dev_warn(dev, "Unknown id %x, using %x\n", dev_id,
502			 chip_info->chip_id);
503	}
504
505	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
506	if (!indio_dev)
507		return -ENOMEM;
508
509	data = iio_priv(indio_dev);
510	data->client = client;
511
512	/* Accept type from dt with default of Type-K. */
513	data->thermocouple_type = THERMOCOUPLE_TYPE_K;
514	ret = device_property_read_u32(dev, "thermocouple-type",
515				       &data->thermocouple_type);
516	if (ret && ret != -EINVAL)
517		return dev_err_probe(dev, ret,
518				     "Error reading thermocouple-type property\n");
519
520	if (data->thermocouple_type >= ARRAY_SIZE(mcp9600_type_map))
521		return dev_err_probe(dev, -EINVAL,
522				     "Invalid thermocouple-type property %u.\n",
523				     data->thermocouple_type);
524
525	/* Set initial config. */
526	ret = mcp9600_config(data);
527	if (ret)
528		return ret;
529
530	ch_sel = mcp9600_probe_alerts(indio_dev);
531	if (ch_sel < 0)
532		return ch_sel;
533
534	indio_dev->info = &mcp9600_info;
535	indio_dev->name = chip_info->chip_name;
536	indio_dev->modes = INDIO_DIRECT_MODE;
537	indio_dev->channels = mcp9600_channels[ch_sel];
538	indio_dev->num_channels = ARRAY_SIZE(mcp9600_channels[ch_sel]);
539
540	return devm_iio_device_register(dev, indio_dev);
541}
542
543static const struct mcp_chip_info mcp9600_chip_info = {
544	.chip_id   = MCP9600_DEVICE_ID_MCP9600,
545	.chip_name = "mcp9600",
546};
547
548static const struct mcp_chip_info mcp9601_chip_info = {
549	.chip_id   = MCP9600_DEVICE_ID_MCP9601,
550	.chip_name = "mcp9601",
551};
552
553static const struct i2c_device_id mcp9600_id[] = {
554	{ "mcp9600", .driver_data = (kernel_ulong_t)&mcp9600_chip_info },
555	{ "mcp9601", .driver_data = (kernel_ulong_t)&mcp9601_chip_info },
556	{ }
557};
558MODULE_DEVICE_TABLE(i2c, mcp9600_id);
559
560static const struct of_device_id mcp9600_of_match[] = {
561	{ .compatible = "microchip,mcp9600", .data = &mcp9600_chip_info },
562	{ .compatible = "microchip,mcp9601", .data = &mcp9601_chip_info },
563	{ }
564};
565MODULE_DEVICE_TABLE(of, mcp9600_of_match);
566
567static struct i2c_driver mcp9600_driver = {
568	.driver = {
569		.name = "mcp9600",
570		.of_match_table = mcp9600_of_match,
571	},
572	.probe = mcp9600_probe,
573	.id_table = mcp9600_id
574};
575module_i2c_driver(mcp9600_driver);
576
577MODULE_AUTHOR("Dimitri Fedrau <dima.fedrau@gmail.com>");
578MODULE_AUTHOR("Andrew Hepp <andrew.hepp@ahepp.dev>");
579MODULE_DESCRIPTION("Microchip MCP9600 thermocouple EMF converter driver");
580MODULE_LICENSE("GPL");
~/ Read-only file preview.