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