Skip to content

Commit 1f25ca1

Browse files
mranostayjic23
authored andcommitted
iio: temperature: add support for Maxim thermocouple chips
Add initial driver support for MAX6675, and MAX31855 thermocouple chips. Cc: Marek Vasut <marex@denx.de> Cc: Matt Porter <mporter@konsulko.com> Signed-off-by: Matt Ranostay <mranostay@gmail.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
1 parent 0ddfd85 commit 1f25ca1

File tree

4 files changed

+317
-0
lines changed

4 files changed

+317
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Maxim thermocouple support
2+
3+
* https://datasheets.maximintegrated.com/en/ds/MAX6675.pdf
4+
* https://datasheets.maximintegrated.com/en/ds/MAX31855.pdf
5+
6+
Required properties:
7+
8+
- compatible: must be "maxim,max31855" or "maxim,max6675"
9+
- reg: SPI chip select number for the device
10+
- spi-max-frequency: must be 4300000
11+
- spi-cpha: must be defined for max6675 to enable SPI mode 1
12+
13+
Refer to spi/spi-bus.txt for generic SPI slave bindings.
14+
15+
Example:
16+
17+
max31855@0 {
18+
compatible = "maxim,max31855";
19+
reg = <0>;
20+
spi-max-frequency = <4300000>;
21+
};

drivers/iio/temperature/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@
33
#
44
menu "Temperature sensors"
55

6+
config MAXIM_THERMOCOUPLE
7+
tristate "Maxim thermocouple sensors"
8+
depends on SPI
9+
help
10+
If you say yes here you get support for the Maxim series of
11+
thermocouple sensors connected via SPI.
12+
13+
Supported sensors:
14+
* MAX6675
15+
* MAX31855
16+
17+
This driver can also be built as a module. If so, the module will
18+
be called maxim_thermocouple.
19+
620
config MLX90614
721
tristate "MLX90614 contact-less infrared sensor"
822
depends on I2C

drivers/iio/temperature/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Makefile for industrial I/O temperature drivers
33
#
44

5+
obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
56
obj-$(CONFIG_MLX90614) += mlx90614.o
67
obj-$(CONFIG_TMP006) += tmp006.o
78
obj-$(CONFIG_TSYS01) += tsys01.o
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
/*
2+
* maxim_thermocouple.c - Support for Maxim thermocouple chips
3+
*
4+
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*/
16+
17+
#include <linux/module.h>
18+
#include <linux/init.h>
19+
#include <linux/mutex.h>
20+
#include <linux/err.h>
21+
#include <linux/spi/spi.h>
22+
#include <linux/iio/iio.h>
23+
#include <linux/iio/trigger.h>
24+
#include <linux/iio/buffer.h>
25+
#include <linux/iio/triggered_buffer.h>
26+
#include <linux/iio/trigger_consumer.h>
27+
28+
#define MAXIM_THERMOCOUPLE_DRV_NAME "maxim_thermocouple"
29+
30+
enum {
31+
MAX6675,
32+
MAX31855,
33+
};
34+
35+
const struct iio_chan_spec max6675_channels[] = {
36+
{ /* thermocouple temperature */
37+
.type = IIO_TEMP,
38+
.info_mask_separate =
39+
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
40+
.scan_index = 0,
41+
.scan_type = {
42+
.sign = 's',
43+
.realbits = 13,
44+
.storagebits = 16,
45+
.shift = 3,
46+
.endianness = IIO_BE,
47+
},
48+
},
49+
IIO_CHAN_SOFT_TIMESTAMP(1),
50+
};
51+
52+
const struct iio_chan_spec max31855_channels[] = {
53+
{ /* thermocouple temperature */
54+
.type = IIO_TEMP,
55+
.address = 2,
56+
.info_mask_separate =
57+
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
58+
.scan_index = 0,
59+
.scan_type = {
60+
.sign = 's',
61+
.realbits = 14,
62+
.storagebits = 16,
63+
.shift = 2,
64+
.endianness = IIO_BE,
65+
},
66+
},
67+
{ /* cold junction temperature */
68+
.type = IIO_TEMP,
69+
.address = 0,
70+
.channel2 = IIO_MOD_TEMP_AMBIENT,
71+
.modified = 1,
72+
.info_mask_separate =
73+
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
74+
.scan_index = 1,
75+
.scan_type = {
76+
.sign = 's',
77+
.realbits = 12,
78+
.storagebits = 16,
79+
.shift = 4,
80+
.endianness = IIO_BE,
81+
},
82+
},
83+
IIO_CHAN_SOFT_TIMESTAMP(2),
84+
};
85+
86+
static const unsigned long max31855_scan_masks[] = {0x3, 0};
87+
88+
struct maxim_thermocouple_chip {
89+
const struct iio_chan_spec *channels;
90+
const unsigned long *scan_masks;
91+
u8 num_channels;
92+
u8 read_size;
93+
94+
/* bit-check for valid input */
95+
u32 status_bit;
96+
};
97+
98+
const struct maxim_thermocouple_chip maxim_thermocouple_chips[] = {
99+
[MAX6675] = {
100+
.channels = max6675_channels,
101+
.num_channels = ARRAY_SIZE(max6675_channels),
102+
.read_size = 2,
103+
.status_bit = BIT(2),
104+
},
105+
[MAX31855] = {
106+
.channels = max31855_channels,
107+
.num_channels = ARRAY_SIZE(max31855_channels),
108+
.read_size = 4,
109+
.scan_masks = max31855_scan_masks,
110+
.status_bit = BIT(16),
111+
},
112+
};
113+
114+
struct maxim_thermocouple_data {
115+
struct spi_device *spi;
116+
const struct maxim_thermocouple_chip *chip;
117+
118+
u8 buffer[16] ____cacheline_aligned;
119+
};
120+
121+
static int maxim_thermocouple_read(struct maxim_thermocouple_data *data,
122+
struct iio_chan_spec const *chan, int *val)
123+
{
124+
unsigned int storage_bytes = data->chip->read_size;
125+
unsigned int shift = chan->scan_type.shift + (chan->address * 8);
126+
unsigned int buf;
127+
int ret;
128+
129+
ret = spi_read(data->spi, (void *) &buf, storage_bytes);
130+
if (ret)
131+
return ret;
132+
133+
switch (storage_bytes) {
134+
case 2:
135+
*val = be16_to_cpu(buf);
136+
break;
137+
case 4:
138+
*val = be32_to_cpu(buf);
139+
break;
140+
}
141+
142+
/* check to be sure this is a valid reading */
143+
if (*val & data->chip->status_bit)
144+
return -EINVAL;
145+
146+
*val = sign_extend32(*val >> shift, chan->scan_type.realbits - 1);
147+
148+
return 0;
149+
}
150+
151+
static irqreturn_t maxim_thermocouple_trigger_handler(int irq, void *private)
152+
{
153+
struct iio_poll_func *pf = private;
154+
struct iio_dev *indio_dev = pf->indio_dev;
155+
struct maxim_thermocouple_data *data = iio_priv(indio_dev);
156+
int ret;
157+
158+
ret = spi_read(data->spi, data->buffer, data->chip->read_size);
159+
if (!ret) {
160+
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
161+
iio_get_time_ns(indio_dev));
162+
}
163+
164+
iio_trigger_notify_done(indio_dev->trig);
165+
166+
return IRQ_HANDLED;
167+
}
168+
169+
static int maxim_thermocouple_read_raw(struct iio_dev *indio_dev,
170+
struct iio_chan_spec const *chan,
171+
int *val, int *val2, long mask)
172+
{
173+
struct maxim_thermocouple_data *data = iio_priv(indio_dev);
174+
int ret = -EINVAL;
175+
176+
switch (mask) {
177+
case IIO_CHAN_INFO_RAW:
178+
ret = iio_device_claim_direct_mode(indio_dev);
179+
if (ret)
180+
return ret;
181+
182+
ret = maxim_thermocouple_read(data, chan, val);
183+
iio_device_release_direct_mode(indio_dev);
184+
185+
if (!ret)
186+
return IIO_VAL_INT;
187+
188+
break;
189+
case IIO_CHAN_INFO_SCALE:
190+
switch (chan->channel2) {
191+
case IIO_MOD_TEMP_AMBIENT:
192+
*val = 62;
193+
*val2 = 500000; /* 1000 * 0.0625 */
194+
ret = IIO_VAL_INT_PLUS_MICRO;
195+
break;
196+
default:
197+
*val = 250; /* 1000 * 0.25 */
198+
ret = IIO_VAL_INT;
199+
};
200+
break;
201+
}
202+
203+
return ret;
204+
}
205+
206+
static const struct iio_info maxim_thermocouple_info = {
207+
.driver_module = THIS_MODULE,
208+
.read_raw = maxim_thermocouple_read_raw,
209+
};
210+
211+
static int maxim_thermocouple_probe(struct spi_device *spi)
212+
{
213+
const struct spi_device_id *id = spi_get_device_id(spi);
214+
struct iio_dev *indio_dev;
215+
struct maxim_thermocouple_data *data;
216+
const struct maxim_thermocouple_chip *chip =
217+
&maxim_thermocouple_chips[id->driver_data];
218+
int ret;
219+
220+
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
221+
if (!indio_dev)
222+
return -ENOMEM;
223+
224+
indio_dev->info = &maxim_thermocouple_info;
225+
indio_dev->name = MAXIM_THERMOCOUPLE_DRV_NAME;
226+
indio_dev->channels = chip->channels;
227+
indio_dev->available_scan_masks = chip->scan_masks;
228+
indio_dev->num_channels = chip->num_channels;
229+
indio_dev->modes = INDIO_DIRECT_MODE;
230+
231+
data = iio_priv(indio_dev);
232+
data->spi = spi;
233+
data->chip = chip;
234+
235+
ret = iio_triggered_buffer_setup(indio_dev, NULL,
236+
maxim_thermocouple_trigger_handler, NULL);
237+
if (ret)
238+
return ret;
239+
240+
ret = iio_device_register(indio_dev);
241+
if (ret)
242+
goto error_unreg_buffer;
243+
244+
return 0;
245+
246+
error_unreg_buffer:
247+
iio_triggered_buffer_cleanup(indio_dev);
248+
249+
return ret;
250+
}
251+
252+
static int maxim_thermocouple_remove(struct spi_device *spi)
253+
{
254+
struct iio_dev *indio_dev = spi_get_drvdata(spi);
255+
256+
iio_device_unregister(indio_dev);
257+
iio_triggered_buffer_cleanup(indio_dev);
258+
259+
return 0;
260+
}
261+
262+
static const struct spi_device_id maxim_thermocouple_id[] = {
263+
{"max6675", MAX6675},
264+
{"max31855", MAX31855},
265+
{},
266+
};
267+
MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id);
268+
269+
static struct spi_driver maxim_thermocouple_driver = {
270+
.driver = {
271+
.name = MAXIM_THERMOCOUPLE_DRV_NAME,
272+
},
273+
.probe = maxim_thermocouple_probe,
274+
.remove = maxim_thermocouple_remove,
275+
.id_table = maxim_thermocouple_id,
276+
};
277+
module_spi_driver(maxim_thermocouple_driver);
278+
279+
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
280+
MODULE_DESCRIPTION("Maxim thermocouple sensors");
281+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)