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 56 *val = sign_extend32(ret, 15); 57 58 return 0; 59 } 60 61 static int mcp9600_read_raw(struct iio_dev *indio_dev, 62 struct iio_chan_spec const *chan, int *val, 63 int *val2, long mask) 64 { 65 struct mcp9600_data *data = iio_priv(indio_dev); 66 int ret; 67 68 switch (mask) { 69 case IIO_CHAN_INFO_RAW: 70 ret = mcp9600_read(data, chan, val); 71 if (ret) 72 return ret; 73 return IIO_VAL_INT; 74 case IIO_CHAN_INFO_SCALE: 75 *val = 62; 76 *val2 = 500000; 77 return IIO_VAL_INT_PLUS_MICRO; 78 default: 79 return -EINVAL; 80 } 81 } 82 83 static const struct iio_info mcp9600_info = { 84 .read_raw = mcp9600_read_raw, 85 }; 86 87 static int mcp9600_probe(struct i2c_client *client) 88 { 89 struct iio_dev *indio_dev; 90 struct mcp9600_data *data; 91 int ret; 92 93 ret = i2c_smbus_read_byte_data(client, MCP9600_DEVICE_ID); 94 if (ret < 0) 95 return dev_err_probe(&client->dev, ret, "Failed to read device ID\n"); 96 if (ret != MCP9600_DEVICE_ID_MCP9600) 97 dev_warn(&client->dev, "Expected ID %x, got %x\n", 98 MCP9600_DEVICE_ID_MCP9600, ret); 99 100 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 101 if (!indio_dev) 102 return -ENOMEM; 103 104 data = iio_priv(indio_dev); 105 data->client = client; 106 107 indio_dev->info = &mcp9600_info; 108 indio_dev->name = "mcp9600"; 109 indio_dev->modes = INDIO_DIRECT_MODE; 110 indio_dev->channels = mcp9600_channels; 111 indio_dev->num_channels = ARRAY_SIZE(mcp9600_channels); 112 113 return devm_iio_device_register(&client->dev, indio_dev); 114 } 115 116 static const struct i2c_device_id mcp9600_id[] = { 117 { "mcp9600" }, 118 {} 119 }; 120 MODULE_DEVICE_TABLE(i2c, mcp9600_id); 121 122 static const struct of_device_id mcp9600_of_match[] = { 123 { .compatible = "microchip,mcp9600" }, 124 {} 125 }; 126 MODULE_DEVICE_TABLE(of, mcp9600_of_match); 127 128 static struct i2c_driver mcp9600_driver = { 129 .driver = { 130 .name = "mcp9600", 131 .of_match_table = mcp9600_of_match, 132 }, 133 .probe = mcp9600_probe, 134 .id_table = mcp9600_id 135 }; 136 module_i2c_driver(mcp9600_driver); 137 138 MODULE_AUTHOR("Andrew Hepp <andrew.hepp@ahepp.dev>"); 139 MODULE_DESCRIPTION("Microchip MCP9600 thermocouple EMF converter driver"); 140 MODULE_LICENSE("GPL"); 141