18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for MAX31730 3-Channel Remote Temperature Sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2019 Guenter Roeck <linux@roeck-us.net> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bits.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/i2c.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Addresses scanned */ 198c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x1c, 0x1d, 0x1e, 0x1f, 0x4c, 208c2ecf20Sopenharmony_ci 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* The MAX31730 registers */ 238c2ecf20Sopenharmony_ci#define MAX31730_REG_TEMP 0x00 248c2ecf20Sopenharmony_ci#define MAX31730_REG_CONF 0x13 258c2ecf20Sopenharmony_ci#define MAX31730_STOP BIT(7) 268c2ecf20Sopenharmony_ci#define MAX31730_EXTRANGE BIT(1) 278c2ecf20Sopenharmony_ci#define MAX31730_REG_TEMP_OFFSET 0x16 288c2ecf20Sopenharmony_ci#define MAX31730_TEMP_OFFSET_BASELINE 0x77 298c2ecf20Sopenharmony_ci#define MAX31730_REG_OFFSET_ENABLE 0x17 308c2ecf20Sopenharmony_ci#define MAX31730_REG_TEMP_MAX 0x20 318c2ecf20Sopenharmony_ci#define MAX31730_REG_TEMP_MIN 0x30 328c2ecf20Sopenharmony_ci#define MAX31730_REG_STATUS_HIGH 0x32 338c2ecf20Sopenharmony_ci#define MAX31730_REG_STATUS_LOW 0x33 348c2ecf20Sopenharmony_ci#define MAX31730_REG_CHANNEL_ENABLE 0x35 358c2ecf20Sopenharmony_ci#define MAX31730_REG_TEMP_FAULT 0x36 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define MAX31730_REG_MFG_ID 0x50 388c2ecf20Sopenharmony_ci#define MAX31730_MFG_ID 0x4d 398c2ecf20Sopenharmony_ci#define MAX31730_REG_MFG_REV 0x51 408c2ecf20Sopenharmony_ci#define MAX31730_MFG_REV 0x01 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define MAX31730_TEMP_MIN (-128000) 438c2ecf20Sopenharmony_ci#define MAX31730_TEMP_MAX 127937 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Each client has this additional data */ 468c2ecf20Sopenharmony_cistruct max31730_data { 478c2ecf20Sopenharmony_ci struct i2c_client *client; 488c2ecf20Sopenharmony_ci u8 orig_conf; 498c2ecf20Sopenharmony_ci u8 current_conf; 508c2ecf20Sopenharmony_ci u8 offset_enable; 518c2ecf20Sopenharmony_ci u8 channel_enable; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/*-----------------------------------------------------------------------*/ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic inline long max31730_reg_to_mc(s16 temp) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST((temp >> 4) * 1000, 16); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int max31730_write_config(struct max31730_data *data, u8 set_mask, 628c2ecf20Sopenharmony_ci u8 clr_mask) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci u8 value; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci clr_mask |= MAX31730_EXTRANGE; 678c2ecf20Sopenharmony_ci value = data->current_conf & ~clr_mask; 688c2ecf20Sopenharmony_ci value |= set_mask; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (data->current_conf != value) { 718c2ecf20Sopenharmony_ci s32 err; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci err = i2c_smbus_write_byte_data(data->client, MAX31730_REG_CONF, 748c2ecf20Sopenharmony_ci value); 758c2ecf20Sopenharmony_ci if (err) 768c2ecf20Sopenharmony_ci return err; 778c2ecf20Sopenharmony_ci data->current_conf = value; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int max31730_set_enable(struct i2c_client *client, int reg, 838c2ecf20Sopenharmony_ci u8 *confdata, int channel, bool enable) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci u8 regval = *confdata; 868c2ecf20Sopenharmony_ci int err; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (enable) 898c2ecf20Sopenharmony_ci regval |= BIT(channel); 908c2ecf20Sopenharmony_ci else 918c2ecf20Sopenharmony_ci regval &= ~BIT(channel); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (regval != *confdata) { 948c2ecf20Sopenharmony_ci err = i2c_smbus_write_byte_data(client, reg, regval); 958c2ecf20Sopenharmony_ci if (err) 968c2ecf20Sopenharmony_ci return err; 978c2ecf20Sopenharmony_ci *confdata = regval; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int max31730_set_offset_enable(struct max31730_data *data, int channel, 1038c2ecf20Sopenharmony_ci bool enable) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci return max31730_set_enable(data->client, MAX31730_REG_OFFSET_ENABLE, 1068c2ecf20Sopenharmony_ci &data->offset_enable, channel, enable); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int max31730_set_channel_enable(struct max31730_data *data, int channel, 1108c2ecf20Sopenharmony_ci bool enable) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return max31730_set_enable(data->client, MAX31730_REG_CHANNEL_ENABLE, 1138c2ecf20Sopenharmony_ci &data->channel_enable, channel, enable); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int max31730_read(struct device *dev, enum hwmon_sensor_types type, 1178c2ecf20Sopenharmony_ci u32 attr, int channel, long *val) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct max31730_data *data = dev_get_drvdata(dev); 1208c2ecf20Sopenharmony_ci int regval, reg, offset; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (type != hwmon_temp) 1238c2ecf20Sopenharmony_ci return -EINVAL; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci switch (attr) { 1268c2ecf20Sopenharmony_ci case hwmon_temp_input: 1278c2ecf20Sopenharmony_ci if (!(data->channel_enable & BIT(channel))) 1288c2ecf20Sopenharmony_ci return -ENODATA; 1298c2ecf20Sopenharmony_ci reg = MAX31730_REG_TEMP + (channel * 2); 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci case hwmon_temp_max: 1328c2ecf20Sopenharmony_ci reg = MAX31730_REG_TEMP_MAX + (channel * 2); 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci case hwmon_temp_min: 1358c2ecf20Sopenharmony_ci reg = MAX31730_REG_TEMP_MIN; 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci case hwmon_temp_enable: 1388c2ecf20Sopenharmony_ci *val = !!(data->channel_enable & BIT(channel)); 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci case hwmon_temp_offset: 1418c2ecf20Sopenharmony_ci if (!channel) 1428c2ecf20Sopenharmony_ci return -EINVAL; 1438c2ecf20Sopenharmony_ci if (!(data->offset_enable & BIT(channel))) { 1448c2ecf20Sopenharmony_ci *val = 0; 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci offset = i2c_smbus_read_byte_data(data->client, 1488c2ecf20Sopenharmony_ci MAX31730_REG_TEMP_OFFSET); 1498c2ecf20Sopenharmony_ci if (offset < 0) 1508c2ecf20Sopenharmony_ci return offset; 1518c2ecf20Sopenharmony_ci *val = (offset - MAX31730_TEMP_OFFSET_BASELINE) * 125; 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci case hwmon_temp_fault: 1548c2ecf20Sopenharmony_ci regval = i2c_smbus_read_byte_data(data->client, 1558c2ecf20Sopenharmony_ci MAX31730_REG_TEMP_FAULT); 1568c2ecf20Sopenharmony_ci if (regval < 0) 1578c2ecf20Sopenharmony_ci return regval; 1588c2ecf20Sopenharmony_ci *val = !!(regval & BIT(channel)); 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci case hwmon_temp_min_alarm: 1618c2ecf20Sopenharmony_ci regval = i2c_smbus_read_byte_data(data->client, 1628c2ecf20Sopenharmony_ci MAX31730_REG_STATUS_LOW); 1638c2ecf20Sopenharmony_ci if (regval < 0) 1648c2ecf20Sopenharmony_ci return regval; 1658c2ecf20Sopenharmony_ci *val = !!(regval & BIT(channel)); 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci case hwmon_temp_max_alarm: 1688c2ecf20Sopenharmony_ci regval = i2c_smbus_read_byte_data(data->client, 1698c2ecf20Sopenharmony_ci MAX31730_REG_STATUS_HIGH); 1708c2ecf20Sopenharmony_ci if (regval < 0) 1718c2ecf20Sopenharmony_ci return regval; 1728c2ecf20Sopenharmony_ci *val = !!(regval & BIT(channel)); 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci default: 1758c2ecf20Sopenharmony_ci return -EINVAL; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci regval = i2c_smbus_read_word_swapped(data->client, reg); 1788c2ecf20Sopenharmony_ci if (regval < 0) 1798c2ecf20Sopenharmony_ci return regval; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci *val = max31730_reg_to_mc(regval); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int max31730_write(struct device *dev, enum hwmon_sensor_types type, 1878c2ecf20Sopenharmony_ci u32 attr, int channel, long val) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct max31730_data *data = dev_get_drvdata(dev); 1908c2ecf20Sopenharmony_ci int reg, err; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (type != hwmon_temp) 1938c2ecf20Sopenharmony_ci return -EINVAL; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci switch (attr) { 1968c2ecf20Sopenharmony_ci case hwmon_temp_max: 1978c2ecf20Sopenharmony_ci reg = MAX31730_REG_TEMP_MAX + channel * 2; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case hwmon_temp_min: 2008c2ecf20Sopenharmony_ci reg = MAX31730_REG_TEMP_MIN; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case hwmon_temp_enable: 2038c2ecf20Sopenharmony_ci if (val != 0 && val != 1) 2048c2ecf20Sopenharmony_ci return -EINVAL; 2058c2ecf20Sopenharmony_ci return max31730_set_channel_enable(data, channel, val); 2068c2ecf20Sopenharmony_ci case hwmon_temp_offset: 2078c2ecf20Sopenharmony_ci val = clamp_val(val, -14875, 17000) + 14875; 2088c2ecf20Sopenharmony_ci val = DIV_ROUND_CLOSEST(val, 125); 2098c2ecf20Sopenharmony_ci err = max31730_set_offset_enable(data, channel, 2108c2ecf20Sopenharmony_ci val != MAX31730_TEMP_OFFSET_BASELINE); 2118c2ecf20Sopenharmony_ci if (err) 2128c2ecf20Sopenharmony_ci return err; 2138c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, 2148c2ecf20Sopenharmony_ci MAX31730_REG_TEMP_OFFSET, val); 2158c2ecf20Sopenharmony_ci default: 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci val = clamp_val(val, MAX31730_TEMP_MIN, MAX31730_TEMP_MAX); 2208c2ecf20Sopenharmony_ci val = DIV_ROUND_CLOSEST(val << 4, 1000) << 4; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return i2c_smbus_write_word_swapped(data->client, reg, (u16)val); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic umode_t max31730_is_visible(const void *data, 2268c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 2278c2ecf20Sopenharmony_ci u32 attr, int channel) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci switch (type) { 2308c2ecf20Sopenharmony_ci case hwmon_temp: 2318c2ecf20Sopenharmony_ci switch (attr) { 2328c2ecf20Sopenharmony_ci case hwmon_temp_input: 2338c2ecf20Sopenharmony_ci case hwmon_temp_min_alarm: 2348c2ecf20Sopenharmony_ci case hwmon_temp_max_alarm: 2358c2ecf20Sopenharmony_ci case hwmon_temp_fault: 2368c2ecf20Sopenharmony_ci return 0444; 2378c2ecf20Sopenharmony_ci case hwmon_temp_min: 2388c2ecf20Sopenharmony_ci return channel ? 0444 : 0644; 2398c2ecf20Sopenharmony_ci case hwmon_temp_offset: 2408c2ecf20Sopenharmony_ci case hwmon_temp_enable: 2418c2ecf20Sopenharmony_ci case hwmon_temp_max: 2428c2ecf20Sopenharmony_ci return 0644; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci default: 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *max31730_info[] = { 2528c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(chip, 2538c2ecf20Sopenharmony_ci HWMON_C_REGISTER_TZ), 2548c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(temp, 2558c2ecf20Sopenharmony_ci HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | 2568c2ecf20Sopenharmony_ci HWMON_T_ENABLE | 2578c2ecf20Sopenharmony_ci HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM, 2588c2ecf20Sopenharmony_ci HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | 2598c2ecf20Sopenharmony_ci HWMON_T_OFFSET | HWMON_T_ENABLE | 2608c2ecf20Sopenharmony_ci HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | 2618c2ecf20Sopenharmony_ci HWMON_T_FAULT, 2628c2ecf20Sopenharmony_ci HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | 2638c2ecf20Sopenharmony_ci HWMON_T_OFFSET | HWMON_T_ENABLE | 2648c2ecf20Sopenharmony_ci HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | 2658c2ecf20Sopenharmony_ci HWMON_T_FAULT, 2668c2ecf20Sopenharmony_ci HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | 2678c2ecf20Sopenharmony_ci HWMON_T_OFFSET | HWMON_T_ENABLE | 2688c2ecf20Sopenharmony_ci HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | 2698c2ecf20Sopenharmony_ci HWMON_T_FAULT 2708c2ecf20Sopenharmony_ci ), 2718c2ecf20Sopenharmony_ci NULL 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic const struct hwmon_ops max31730_hwmon_ops = { 2758c2ecf20Sopenharmony_ci .is_visible = max31730_is_visible, 2768c2ecf20Sopenharmony_ci .read = max31730_read, 2778c2ecf20Sopenharmony_ci .write = max31730_write, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info max31730_chip_info = { 2818c2ecf20Sopenharmony_ci .ops = &max31730_hwmon_ops, 2828c2ecf20Sopenharmony_ci .info = max31730_info, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic void max31730_remove(void *data) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct max31730_data *max31730 = data; 2888c2ecf20Sopenharmony_ci struct i2c_client *client = max31730->client; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, MAX31730_REG_CONF, 2918c2ecf20Sopenharmony_ci max31730->orig_conf); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int 2958c2ecf20Sopenharmony_cimax31730_probe(struct i2c_client *client) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 2988c2ecf20Sopenharmony_ci struct device *hwmon_dev; 2998c2ecf20Sopenharmony_ci struct max31730_data *data; 3008c2ecf20Sopenharmony_ci int status, err; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, 3038c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) 3048c2ecf20Sopenharmony_ci return -EIO; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct max31730_data), GFP_KERNEL); 3078c2ecf20Sopenharmony_ci if (!data) 3088c2ecf20Sopenharmony_ci return -ENOMEM; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci data->client = client; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* Cache original configuration and enable status */ 3138c2ecf20Sopenharmony_ci status = i2c_smbus_read_byte_data(client, MAX31730_REG_CHANNEL_ENABLE); 3148c2ecf20Sopenharmony_ci if (status < 0) 3158c2ecf20Sopenharmony_ci return status; 3168c2ecf20Sopenharmony_ci data->channel_enable = status; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci status = i2c_smbus_read_byte_data(client, MAX31730_REG_OFFSET_ENABLE); 3198c2ecf20Sopenharmony_ci if (status < 0) 3208c2ecf20Sopenharmony_ci return status; 3218c2ecf20Sopenharmony_ci data->offset_enable = status; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci status = i2c_smbus_read_byte_data(client, MAX31730_REG_CONF); 3248c2ecf20Sopenharmony_ci if (status < 0) 3258c2ecf20Sopenharmony_ci return status; 3268c2ecf20Sopenharmony_ci data->orig_conf = status; 3278c2ecf20Sopenharmony_ci data->current_conf = status; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci err = max31730_write_config(data, 3308c2ecf20Sopenharmony_ci data->channel_enable ? 0 : MAX31730_STOP, 3318c2ecf20Sopenharmony_ci data->channel_enable ? MAX31730_STOP : 0); 3328c2ecf20Sopenharmony_ci if (err) 3338c2ecf20Sopenharmony_ci return err; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci dev_set_drvdata(dev, data); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci err = devm_add_action_or_reset(dev, max31730_remove, data); 3388c2ecf20Sopenharmony_ci if (err) 3398c2ecf20Sopenharmony_ci return err; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, 3428c2ecf20Sopenharmony_ci data, 3438c2ecf20Sopenharmony_ci &max31730_chip_info, 3448c2ecf20Sopenharmony_ci NULL); 3458c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic const struct i2c_device_id max31730_ids[] = { 3498c2ecf20Sopenharmony_ci { "max31730", 0, }, 3508c2ecf20Sopenharmony_ci { } 3518c2ecf20Sopenharmony_ci}; 3528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max31730_ids); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic const struct of_device_id __maybe_unused max31730_of_match[] = { 3558c2ecf20Sopenharmony_ci { 3568c2ecf20Sopenharmony_ci .compatible = "maxim,max31730", 3578c2ecf20Sopenharmony_ci }, 3588c2ecf20Sopenharmony_ci { }, 3598c2ecf20Sopenharmony_ci}; 3608c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, max31730_of_match); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic bool max31730_check_reg_temp(struct i2c_client *client, 3638c2ecf20Sopenharmony_ci int reg) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci int regval; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci regval = i2c_smbus_read_byte_data(client, reg + 1); 3688c2ecf20Sopenharmony_ci return regval < 0 || (regval & 0x0f); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */ 3728c2ecf20Sopenharmony_cistatic int max31730_detect(struct i2c_client *client, 3738c2ecf20Sopenharmony_ci struct i2c_board_info *info) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 3768c2ecf20Sopenharmony_ci int regval; 3778c2ecf20Sopenharmony_ci int i; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | 3808c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_WORD_DATA)) 3818c2ecf20Sopenharmony_ci return -ENODEV; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci regval = i2c_smbus_read_byte_data(client, MAX31730_REG_MFG_ID); 3848c2ecf20Sopenharmony_ci if (regval != MAX31730_MFG_ID) 3858c2ecf20Sopenharmony_ci return -ENODEV; 3868c2ecf20Sopenharmony_ci regval = i2c_smbus_read_byte_data(client, MAX31730_REG_MFG_REV); 3878c2ecf20Sopenharmony_ci if (regval != MAX31730_MFG_REV) 3888c2ecf20Sopenharmony_ci return -ENODEV; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* lower 4 bit of temperature and limit registers must be 0 */ 3918c2ecf20Sopenharmony_ci if (max31730_check_reg_temp(client, MAX31730_REG_TEMP_MIN)) 3928c2ecf20Sopenharmony_ci return -ENODEV; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 3958c2ecf20Sopenharmony_ci if (max31730_check_reg_temp(client, MAX31730_REG_TEMP + i * 2)) 3968c2ecf20Sopenharmony_ci return -ENODEV; 3978c2ecf20Sopenharmony_ci if (max31730_check_reg_temp(client, 3988c2ecf20Sopenharmony_ci MAX31730_REG_TEMP_MAX + i * 2)) 3998c2ecf20Sopenharmony_ci return -ENODEV; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci strlcpy(info->type, "max31730", I2C_NAME_SIZE); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int __maybe_unused max31730_suspend(struct device *dev) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct max31730_data *data = dev_get_drvdata(dev); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return max31730_write_config(data, MAX31730_STOP, 0); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int __maybe_unused max31730_resume(struct device *dev) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct max31730_data *data = dev_get_drvdata(dev); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return max31730_write_config(data, 0, MAX31730_STOP); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(max31730_pm_ops, max31730_suspend, max31730_resume); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic struct i2c_driver max31730_driver = { 4248c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 4258c2ecf20Sopenharmony_ci .driver = { 4268c2ecf20Sopenharmony_ci .name = "max31730", 4278c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(max31730_of_match), 4288c2ecf20Sopenharmony_ci .pm = &max31730_pm_ops, 4298c2ecf20Sopenharmony_ci }, 4308c2ecf20Sopenharmony_ci .probe_new = max31730_probe, 4318c2ecf20Sopenharmony_ci .id_table = max31730_ids, 4328c2ecf20Sopenharmony_ci .detect = max31730_detect, 4338c2ecf20Sopenharmony_ci .address_list = normal_i2c, 4348c2ecf20Sopenharmony_ci}; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cimodule_i2c_driver(max31730_driver); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); 4398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAX31730 driver"); 4408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 441