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/err.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/init.h> |
12 | #include <linux/mod_devicetable.h> |
13 | #include <linux/module.h> |
14 | |
15 | #include <linux/iio/iio.h> |
16 | |
17 | /* MCP9600 registers */ |
18 | #define MCP9600_HOT_JUNCTION 0x0 |
19 | #define MCP9600_COLD_JUNCTION 0x2 |
20 | #define MCP9600_DEVICE_ID 0x20 |
21 | |
22 | /* MCP9600 device id value */ |
23 | #define MCP9600_DEVICE_ID_MCP9600 0x40 |
24 | |
25 | static const struct iio_chan_spec mcp9600_channels[] = { |
26 | { |
27 | .type = IIO_TEMP, |
28 | .address = MCP9600_HOT_JUNCTION, |
29 | .info_mask_separate = |
30 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), |
31 | }, |
32 | { |
33 | .type = IIO_TEMP, |
34 | .address = MCP9600_COLD_JUNCTION, |
35 | .channel2 = IIO_MOD_TEMP_AMBIENT, |
36 | .modified = 1, |
37 | .info_mask_separate = |
38 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), |
39 | }, |
40 | }; |
41 | |
42 | struct mcp9600_data { |
43 | struct i2c_client *client; |
44 | }; |
45 | |
46 | static int mcp9600_read(struct mcp9600_data *data, |
47 | struct iio_chan_spec const *chan, int *val) |
48 | { |
49 | int ret; |
50 | |
51 | ret = i2c_smbus_read_word_swapped(client: data->client, command: chan->address); |
52 | |
53 | if (ret < 0) |
54 | return ret; |
55 | *val = ret; |
56 | |
57 | return 0; |
58 | } |
59 | |
60 | static int mcp9600_read_raw(struct iio_dev *indio_dev, |
61 | struct iio_chan_spec const *chan, int *val, |
62 | int *val2, long mask) |
63 | { |
64 | struct mcp9600_data *data = iio_priv(indio_dev); |
65 | int ret; |
66 | |
67 | switch (mask) { |
68 | case IIO_CHAN_INFO_RAW: |
69 | ret = mcp9600_read(data, chan, val); |
70 | if (ret) |
71 | return ret; |
72 | return IIO_VAL_INT; |
73 | case IIO_CHAN_INFO_SCALE: |
74 | *val = 62; |
75 | *val2 = 500000; |
76 | return IIO_VAL_INT_PLUS_MICRO; |
77 | default: |
78 | return -EINVAL; |
79 | } |
80 | } |
81 | |
82 | static const struct iio_info mcp9600_info = { |
83 | .read_raw = mcp9600_read_raw, |
84 | }; |
85 | |
86 | static int mcp9600_probe(struct i2c_client *client) |
87 | { |
88 | struct iio_dev *indio_dev; |
89 | struct mcp9600_data *data; |
90 | int ret; |
91 | |
92 | ret = i2c_smbus_read_byte_data(client, MCP9600_DEVICE_ID); |
93 | if (ret < 0) |
94 | return dev_err_probe(dev: &client->dev, err: ret, fmt: "Failed to read device ID\n" ); |
95 | if (ret != MCP9600_DEVICE_ID_MCP9600) |
96 | dev_warn(&client->dev, "Expected ID %x, got %x\n" , |
97 | MCP9600_DEVICE_ID_MCP9600, ret); |
98 | |
99 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
100 | if (!indio_dev) |
101 | return -ENOMEM; |
102 | |
103 | data = iio_priv(indio_dev); |
104 | data->client = client; |
105 | |
106 | indio_dev->info = &mcp9600_info; |
107 | indio_dev->name = "mcp9600" ; |
108 | indio_dev->modes = INDIO_DIRECT_MODE; |
109 | indio_dev->channels = mcp9600_channels; |
110 | indio_dev->num_channels = ARRAY_SIZE(mcp9600_channels); |
111 | |
112 | return devm_iio_device_register(&client->dev, indio_dev); |
113 | } |
114 | |
115 | static const struct i2c_device_id mcp9600_id[] = { |
116 | { "mcp9600" }, |
117 | {} |
118 | }; |
119 | MODULE_DEVICE_TABLE(i2c, mcp9600_id); |
120 | |
121 | static const struct of_device_id mcp9600_of_match[] = { |
122 | { .compatible = "microchip,mcp9600" }, |
123 | {} |
124 | }; |
125 | MODULE_DEVICE_TABLE(of, mcp9600_of_match); |
126 | |
127 | static struct i2c_driver mcp9600_driver = { |
128 | .driver = { |
129 | .name = "mcp9600" , |
130 | .of_match_table = mcp9600_of_match, |
131 | }, |
132 | .probe = mcp9600_probe, |
133 | .id_table = mcp9600_id |
134 | }; |
135 | module_i2c_driver(mcp9600_driver); |
136 | |
137 | MODULE_AUTHOR("Andrew Hepp <andrew.hepp@ahepp.dev>" ); |
138 | MODULE_DESCRIPTION("Microchip MCP9600 thermocouple EMF converter driver" ); |
139 | MODULE_LICENSE("GPL" ); |
140 | |