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(data->client, 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(&client->dev, ret, "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(&client->dev, 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