18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The LM95241 is a sensor chip made by National Semiconductors. 68c2ecf20Sopenharmony_ci * It reports up to three temperatures (its own plus up to two external ones). 78c2ecf20Sopenharmony_ci * Complete datasheet can be obtained from National's website at: 88c2ecf20Sopenharmony_ci * http://www.national.com/ds.cgi/LM/LM95241.pdf 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/bitops.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 168c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DEVNAME "lm95241" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 248c2ecf20Sopenharmony_ci 0x19, 0x2a, 0x2b, I2C_CLIENT_END }; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* LM95241 registers */ 278c2ecf20Sopenharmony_ci#define LM95241_REG_R_MAN_ID 0xFE 288c2ecf20Sopenharmony_ci#define LM95241_REG_R_CHIP_ID 0xFF 298c2ecf20Sopenharmony_ci#define LM95241_REG_R_STATUS 0x02 308c2ecf20Sopenharmony_ci#define LM95241_REG_RW_CONFIG 0x03 318c2ecf20Sopenharmony_ci#define LM95241_REG_RW_REM_FILTER 0x06 328c2ecf20Sopenharmony_ci#define LM95241_REG_RW_TRUTHERM 0x07 338c2ecf20Sopenharmony_ci#define LM95241_REG_W_ONE_SHOT 0x0F 348c2ecf20Sopenharmony_ci#define LM95241_REG_R_LOCAL_TEMPH 0x10 358c2ecf20Sopenharmony_ci#define LM95241_REG_R_REMOTE1_TEMPH 0x11 368c2ecf20Sopenharmony_ci#define LM95241_REG_R_REMOTE2_TEMPH 0x12 378c2ecf20Sopenharmony_ci#define LM95241_REG_R_LOCAL_TEMPL 0x20 388c2ecf20Sopenharmony_ci#define LM95241_REG_R_REMOTE1_TEMPL 0x21 398c2ecf20Sopenharmony_ci#define LM95241_REG_R_REMOTE2_TEMPL 0x22 408c2ecf20Sopenharmony_ci#define LM95241_REG_RW_REMOTE_MODEL 0x30 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* LM95241 specific bitfields */ 438c2ecf20Sopenharmony_ci#define CFG_STOP BIT(6) 448c2ecf20Sopenharmony_ci#define CFG_CR0076 0x00 458c2ecf20Sopenharmony_ci#define CFG_CR0182 BIT(4) 468c2ecf20Sopenharmony_ci#define CFG_CR1000 BIT(5) 478c2ecf20Sopenharmony_ci#define CFG_CR2700 (BIT(4) | BIT(5)) 488c2ecf20Sopenharmony_ci#define CFG_CRMASK (BIT(4) | BIT(5)) 498c2ecf20Sopenharmony_ci#define R1MS_MASK BIT(0) 508c2ecf20Sopenharmony_ci#define R2MS_MASK BIT(2) 518c2ecf20Sopenharmony_ci#define R1DF_MASK BIT(1) 528c2ecf20Sopenharmony_ci#define R2DF_MASK BIT(2) 538c2ecf20Sopenharmony_ci#define R1FE_MASK BIT(0) 548c2ecf20Sopenharmony_ci#define R2FE_MASK BIT(2) 558c2ecf20Sopenharmony_ci#define R1DM BIT(0) 568c2ecf20Sopenharmony_ci#define R2DM BIT(1) 578c2ecf20Sopenharmony_ci#define TT1_SHIFT 0 588c2ecf20Sopenharmony_ci#define TT2_SHIFT 4 598c2ecf20Sopenharmony_ci#define TT_OFF 0 608c2ecf20Sopenharmony_ci#define TT_ON 1 618c2ecf20Sopenharmony_ci#define TT_MASK 7 628c2ecf20Sopenharmony_ci#define NATSEMI_MAN_ID 0x01 638c2ecf20Sopenharmony_ci#define LM95231_CHIP_ID 0xA1 648c2ecf20Sopenharmony_ci#define LM95241_CHIP_ID 0xA4 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic const u8 lm95241_reg_address[] = { 678c2ecf20Sopenharmony_ci LM95241_REG_R_LOCAL_TEMPH, 688c2ecf20Sopenharmony_ci LM95241_REG_R_LOCAL_TEMPL, 698c2ecf20Sopenharmony_ci LM95241_REG_R_REMOTE1_TEMPH, 708c2ecf20Sopenharmony_ci LM95241_REG_R_REMOTE1_TEMPL, 718c2ecf20Sopenharmony_ci LM95241_REG_R_REMOTE2_TEMPH, 728c2ecf20Sopenharmony_ci LM95241_REG_R_REMOTE2_TEMPL 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Client data (each client gets its own) */ 768c2ecf20Sopenharmony_cistruct lm95241_data { 778c2ecf20Sopenharmony_ci struct i2c_client *client; 788c2ecf20Sopenharmony_ci struct mutex update_lock; 798c2ecf20Sopenharmony_ci unsigned long last_updated; /* in jiffies */ 808c2ecf20Sopenharmony_ci unsigned long interval; /* in milli-seconds */ 818c2ecf20Sopenharmony_ci char valid; /* zero until following fields are valid */ 828c2ecf20Sopenharmony_ci /* registers values */ 838c2ecf20Sopenharmony_ci u8 temp[ARRAY_SIZE(lm95241_reg_address)]; 848c2ecf20Sopenharmony_ci u8 status, config, model, trutherm; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* Conversions */ 888c2ecf20Sopenharmony_cistatic int temp_from_reg_signed(u8 val_h, u8 val_l) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci s16 val_hl = (val_h << 8) | val_l; 918c2ecf20Sopenharmony_ci return val_hl * 1000 / 256; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int temp_from_reg_unsigned(u8 val_h, u8 val_l) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci u16 val_hl = (val_h << 8) | val_l; 978c2ecf20Sopenharmony_ci return val_hl * 1000 / 256; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic struct lm95241_data *lm95241_update_device(struct device *dev) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct lm95241_data *data = dev_get_drvdata(dev); 1038c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated 1088c2ecf20Sopenharmony_ci + msecs_to_jiffies(data->interval)) || 1098c2ecf20Sopenharmony_ci !data->valid) { 1108c2ecf20Sopenharmony_ci int i; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci dev_dbg(dev, "Updating lm95241 data.\n"); 1138c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lm95241_reg_address); i++) 1148c2ecf20Sopenharmony_ci data->temp[i] 1158c2ecf20Sopenharmony_ci = i2c_smbus_read_byte_data(client, 1168c2ecf20Sopenharmony_ci lm95241_reg_address[i]); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci data->status = i2c_smbus_read_byte_data(client, 1198c2ecf20Sopenharmony_ci LM95241_REG_R_STATUS); 1208c2ecf20Sopenharmony_ci data->last_updated = jiffies; 1218c2ecf20Sopenharmony_ci data->valid = 1; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return data; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int lm95241_read_chip(struct device *dev, u32 attr, int channel, 1308c2ecf20Sopenharmony_ci long *val) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct lm95241_data *data = dev_get_drvdata(dev); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci switch (attr) { 1358c2ecf20Sopenharmony_ci case hwmon_chip_update_interval: 1368c2ecf20Sopenharmony_ci *val = data->interval; 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci default: 1398c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int lm95241_read_temp(struct device *dev, u32 attr, int channel, 1448c2ecf20Sopenharmony_ci long *val) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct lm95241_data *data = lm95241_update_device(dev); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci switch (attr) { 1498c2ecf20Sopenharmony_ci case hwmon_temp_input: 1508c2ecf20Sopenharmony_ci if (!channel || (data->config & BIT(channel - 1))) 1518c2ecf20Sopenharmony_ci *val = temp_from_reg_signed(data->temp[channel * 2], 1528c2ecf20Sopenharmony_ci data->temp[channel * 2 + 1]); 1538c2ecf20Sopenharmony_ci else 1548c2ecf20Sopenharmony_ci *val = temp_from_reg_unsigned(data->temp[channel * 2], 1558c2ecf20Sopenharmony_ci data->temp[channel * 2 + 1]); 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci case hwmon_temp_min: 1588c2ecf20Sopenharmony_ci if (channel == 1) 1598c2ecf20Sopenharmony_ci *val = (data->config & R1DF_MASK) ? -128000 : 0; 1608c2ecf20Sopenharmony_ci else 1618c2ecf20Sopenharmony_ci *val = (data->config & R2DF_MASK) ? -128000 : 0; 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci case hwmon_temp_max: 1648c2ecf20Sopenharmony_ci if (channel == 1) 1658c2ecf20Sopenharmony_ci *val = (data->config & R1DF_MASK) ? 127875 : 255875; 1668c2ecf20Sopenharmony_ci else 1678c2ecf20Sopenharmony_ci *val = (data->config & R2DF_MASK) ? 127875 : 255875; 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci case hwmon_temp_type: 1708c2ecf20Sopenharmony_ci if (channel == 1) 1718c2ecf20Sopenharmony_ci *val = (data->model & R1MS_MASK) ? 1 : 2; 1728c2ecf20Sopenharmony_ci else 1738c2ecf20Sopenharmony_ci *val = (data->model & R2MS_MASK) ? 1 : 2; 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci case hwmon_temp_fault: 1768c2ecf20Sopenharmony_ci if (channel == 1) 1778c2ecf20Sopenharmony_ci *val = !!(data->status & R1DM); 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci *val = !!(data->status & R2DM); 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci default: 1828c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int lm95241_read(struct device *dev, enum hwmon_sensor_types type, 1878c2ecf20Sopenharmony_ci u32 attr, int channel, long *val) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci switch (type) { 1908c2ecf20Sopenharmony_ci case hwmon_chip: 1918c2ecf20Sopenharmony_ci return lm95241_read_chip(dev, attr, channel, val); 1928c2ecf20Sopenharmony_ci case hwmon_temp: 1938c2ecf20Sopenharmony_ci return lm95241_read_temp(dev, attr, channel, val); 1948c2ecf20Sopenharmony_ci default: 1958c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int lm95241_write_chip(struct device *dev, u32 attr, int channel, 2008c2ecf20Sopenharmony_ci long val) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct lm95241_data *data = dev_get_drvdata(dev); 2038c2ecf20Sopenharmony_ci int convrate; 2048c2ecf20Sopenharmony_ci u8 config; 2058c2ecf20Sopenharmony_ci int ret; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci switch (attr) { 2108c2ecf20Sopenharmony_ci case hwmon_chip_update_interval: 2118c2ecf20Sopenharmony_ci config = data->config & ~CFG_CRMASK; 2128c2ecf20Sopenharmony_ci if (val < 130) { 2138c2ecf20Sopenharmony_ci convrate = 76; 2148c2ecf20Sopenharmony_ci config |= CFG_CR0076; 2158c2ecf20Sopenharmony_ci } else if (val < 590) { 2168c2ecf20Sopenharmony_ci convrate = 182; 2178c2ecf20Sopenharmony_ci config |= CFG_CR0182; 2188c2ecf20Sopenharmony_ci } else if (val < 1850) { 2198c2ecf20Sopenharmony_ci convrate = 1000; 2208c2ecf20Sopenharmony_ci config |= CFG_CR1000; 2218c2ecf20Sopenharmony_ci } else { 2228c2ecf20Sopenharmony_ci convrate = 2700; 2238c2ecf20Sopenharmony_ci config |= CFG_CR2700; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci data->interval = convrate; 2268c2ecf20Sopenharmony_ci data->config = config; 2278c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 2288c2ecf20Sopenharmony_ci LM95241_REG_RW_CONFIG, config); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci default: 2318c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int lm95241_write_temp(struct device *dev, u32 attr, int channel, 2398c2ecf20Sopenharmony_ci long val) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct lm95241_data *data = dev_get_drvdata(dev); 2428c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 2438c2ecf20Sopenharmony_ci int ret; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci switch (attr) { 2488c2ecf20Sopenharmony_ci case hwmon_temp_min: 2498c2ecf20Sopenharmony_ci if (channel == 1) { 2508c2ecf20Sopenharmony_ci if (val < 0) 2518c2ecf20Sopenharmony_ci data->config |= R1DF_MASK; 2528c2ecf20Sopenharmony_ci else 2538c2ecf20Sopenharmony_ci data->config &= ~R1DF_MASK; 2548c2ecf20Sopenharmony_ci } else { 2558c2ecf20Sopenharmony_ci if (val < 0) 2568c2ecf20Sopenharmony_ci data->config |= R2DF_MASK; 2578c2ecf20Sopenharmony_ci else 2588c2ecf20Sopenharmony_ci data->config &= ~R2DF_MASK; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci data->valid = 0; 2618c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, 2628c2ecf20Sopenharmony_ci data->config); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci case hwmon_temp_max: 2658c2ecf20Sopenharmony_ci if (channel == 1) { 2668c2ecf20Sopenharmony_ci if (val <= 127875) 2678c2ecf20Sopenharmony_ci data->config |= R1DF_MASK; 2688c2ecf20Sopenharmony_ci else 2698c2ecf20Sopenharmony_ci data->config &= ~R1DF_MASK; 2708c2ecf20Sopenharmony_ci } else { 2718c2ecf20Sopenharmony_ci if (val <= 127875) 2728c2ecf20Sopenharmony_ci data->config |= R2DF_MASK; 2738c2ecf20Sopenharmony_ci else 2748c2ecf20Sopenharmony_ci data->config &= ~R2DF_MASK; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci data->valid = 0; 2778c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, 2788c2ecf20Sopenharmony_ci data->config); 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci case hwmon_temp_type: 2818c2ecf20Sopenharmony_ci if (val != 1 && val != 2) { 2828c2ecf20Sopenharmony_ci ret = -EINVAL; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci if (channel == 1) { 2868c2ecf20Sopenharmony_ci data->trutherm &= ~(TT_MASK << TT1_SHIFT); 2878c2ecf20Sopenharmony_ci if (val == 1) { 2888c2ecf20Sopenharmony_ci data->model |= R1MS_MASK; 2898c2ecf20Sopenharmony_ci data->trutherm |= (TT_ON << TT1_SHIFT); 2908c2ecf20Sopenharmony_ci } else { 2918c2ecf20Sopenharmony_ci data->model &= ~R1MS_MASK; 2928c2ecf20Sopenharmony_ci data->trutherm |= (TT_OFF << TT1_SHIFT); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci } else { 2958c2ecf20Sopenharmony_ci data->trutherm &= ~(TT_MASK << TT2_SHIFT); 2968c2ecf20Sopenharmony_ci if (val == 1) { 2978c2ecf20Sopenharmony_ci data->model |= R2MS_MASK; 2988c2ecf20Sopenharmony_ci data->trutherm |= (TT_ON << TT2_SHIFT); 2998c2ecf20Sopenharmony_ci } else { 3008c2ecf20Sopenharmony_ci data->model &= ~R2MS_MASK; 3018c2ecf20Sopenharmony_ci data->trutherm |= (TT_OFF << TT2_SHIFT); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, 3058c2ecf20Sopenharmony_ci LM95241_REG_RW_REMOTE_MODEL, 3068c2ecf20Sopenharmony_ci data->model); 3078c2ecf20Sopenharmony_ci if (ret < 0) 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM, 3108c2ecf20Sopenharmony_ci data->trutherm); 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci default: 3138c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int lm95241_write(struct device *dev, enum hwmon_sensor_types type, 3238c2ecf20Sopenharmony_ci u32 attr, int channel, long val) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci switch (type) { 3268c2ecf20Sopenharmony_ci case hwmon_chip: 3278c2ecf20Sopenharmony_ci return lm95241_write_chip(dev, attr, channel, val); 3288c2ecf20Sopenharmony_ci case hwmon_temp: 3298c2ecf20Sopenharmony_ci return lm95241_write_temp(dev, attr, channel, val); 3308c2ecf20Sopenharmony_ci default: 3318c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic umode_t lm95241_is_visible(const void *data, 3368c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 3378c2ecf20Sopenharmony_ci u32 attr, int channel) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci switch (type) { 3408c2ecf20Sopenharmony_ci case hwmon_chip: 3418c2ecf20Sopenharmony_ci switch (attr) { 3428c2ecf20Sopenharmony_ci case hwmon_chip_update_interval: 3438c2ecf20Sopenharmony_ci return 0644; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci case hwmon_temp: 3478c2ecf20Sopenharmony_ci switch (attr) { 3488c2ecf20Sopenharmony_ci case hwmon_temp_input: 3498c2ecf20Sopenharmony_ci return 0444; 3508c2ecf20Sopenharmony_ci case hwmon_temp_fault: 3518c2ecf20Sopenharmony_ci return 0444; 3528c2ecf20Sopenharmony_ci case hwmon_temp_min: 3538c2ecf20Sopenharmony_ci case hwmon_temp_max: 3548c2ecf20Sopenharmony_ci case hwmon_temp_type: 3558c2ecf20Sopenharmony_ci return 0644; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci default: 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */ 3658c2ecf20Sopenharmony_cistatic int lm95241_detect(struct i2c_client *new_client, 3668c2ecf20Sopenharmony_ci struct i2c_board_info *info) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = new_client->adapter; 3698c2ecf20Sopenharmony_ci const char *name; 3708c2ecf20Sopenharmony_ci int mfg_id, chip_id; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 3738c2ecf20Sopenharmony_ci return -ENODEV; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci mfg_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID); 3768c2ecf20Sopenharmony_ci if (mfg_id != NATSEMI_MAN_ID) 3778c2ecf20Sopenharmony_ci return -ENODEV; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci chip_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID); 3808c2ecf20Sopenharmony_ci switch (chip_id) { 3818c2ecf20Sopenharmony_ci case LM95231_CHIP_ID: 3828c2ecf20Sopenharmony_ci name = "lm95231"; 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci case LM95241_CHIP_ID: 3858c2ecf20Sopenharmony_ci name = "lm95241"; 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci default: 3888c2ecf20Sopenharmony_ci return -ENODEV; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* Fill the i2c board info */ 3928c2ecf20Sopenharmony_ci strlcpy(info->type, name, I2C_NAME_SIZE); 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic void lm95241_init_client(struct i2c_client *client, 3978c2ecf20Sopenharmony_ci struct lm95241_data *data) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci data->interval = 1000; 4008c2ecf20Sopenharmony_ci data->config = CFG_CR1000; 4018c2ecf20Sopenharmony_ci data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); 4048c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, LM95241_REG_RW_REM_FILTER, 4058c2ecf20Sopenharmony_ci R1FE_MASK | R2FE_MASK); 4068c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM, 4078c2ecf20Sopenharmony_ci data->trutherm); 4088c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, LM95241_REG_RW_REMOTE_MODEL, 4098c2ecf20Sopenharmony_ci data->model); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *lm95241_info[] = { 4138c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(chip, 4148c2ecf20Sopenharmony_ci HWMON_C_UPDATE_INTERVAL), 4158c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(temp, 4168c2ecf20Sopenharmony_ci HWMON_T_INPUT, 4178c2ecf20Sopenharmony_ci HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 4188c2ecf20Sopenharmony_ci HWMON_T_TYPE | HWMON_T_FAULT, 4198c2ecf20Sopenharmony_ci HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 4208c2ecf20Sopenharmony_ci HWMON_T_TYPE | HWMON_T_FAULT), 4218c2ecf20Sopenharmony_ci NULL 4228c2ecf20Sopenharmony_ci}; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic const struct hwmon_ops lm95241_hwmon_ops = { 4258c2ecf20Sopenharmony_ci .is_visible = lm95241_is_visible, 4268c2ecf20Sopenharmony_ci .read = lm95241_read, 4278c2ecf20Sopenharmony_ci .write = lm95241_write, 4288c2ecf20Sopenharmony_ci}; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info lm95241_chip_info = { 4318c2ecf20Sopenharmony_ci .ops = &lm95241_hwmon_ops, 4328c2ecf20Sopenharmony_ci .info = lm95241_info, 4338c2ecf20Sopenharmony_ci}; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic int lm95241_probe(struct i2c_client *client) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 4388c2ecf20Sopenharmony_ci struct lm95241_data *data; 4398c2ecf20Sopenharmony_ci struct device *hwmon_dev; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct lm95241_data), GFP_KERNEL); 4428c2ecf20Sopenharmony_ci if (!data) 4438c2ecf20Sopenharmony_ci return -ENOMEM; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci data->client = client; 4468c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Initialize the LM95241 chip */ 4498c2ecf20Sopenharmony_ci lm95241_init_client(client, data); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, 4528c2ecf20Sopenharmony_ci data, 4538c2ecf20Sopenharmony_ci &lm95241_chip_info, 4548c2ecf20Sopenharmony_ci NULL); 4558c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/* Driver data (common to all clients) */ 4598c2ecf20Sopenharmony_cistatic const struct i2c_device_id lm95241_id[] = { 4608c2ecf20Sopenharmony_ci { "lm95231", 0 }, 4618c2ecf20Sopenharmony_ci { "lm95241", 0 }, 4628c2ecf20Sopenharmony_ci { } 4638c2ecf20Sopenharmony_ci}; 4648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lm95241_id); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic struct i2c_driver lm95241_driver = { 4678c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 4688c2ecf20Sopenharmony_ci .driver = { 4698c2ecf20Sopenharmony_ci .name = DEVNAME, 4708c2ecf20Sopenharmony_ci }, 4718c2ecf20Sopenharmony_ci .probe_new = lm95241_probe, 4728c2ecf20Sopenharmony_ci .id_table = lm95241_id, 4738c2ecf20Sopenharmony_ci .detect = lm95241_detect, 4748c2ecf20Sopenharmony_ci .address_list = normal_i2c, 4758c2ecf20Sopenharmony_ci}; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cimodule_i2c_driver(lm95241_driver); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Davide Rizzo <elpa.rizzo@gmail.com>"); 4808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LM95231/LM95241 sensor driver"); 4818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 482