18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * emc6w201.c - Hardware monitoring driver for the SMSC EMC6W201 48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 138c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * Addresses to scan 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * The EMC6W201 registers 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define EMC6W201_REG_IN(nr) (0x20 + (nr)) 288c2ecf20Sopenharmony_ci#define EMC6W201_REG_TEMP(nr) (0x26 + (nr)) 298c2ecf20Sopenharmony_ci#define EMC6W201_REG_FAN(nr) (0x2C + (nr) * 2) 308c2ecf20Sopenharmony_ci#define EMC6W201_REG_COMPANY 0x3E 318c2ecf20Sopenharmony_ci#define EMC6W201_REG_VERSTEP 0x3F 328c2ecf20Sopenharmony_ci#define EMC6W201_REG_CONFIG 0x40 338c2ecf20Sopenharmony_ci#define EMC6W201_REG_IN_LOW(nr) (0x4A + (nr) * 2) 348c2ecf20Sopenharmony_ci#define EMC6W201_REG_IN_HIGH(nr) (0x4B + (nr) * 2) 358c2ecf20Sopenharmony_ci#define EMC6W201_REG_TEMP_LOW(nr) (0x56 + (nr) * 2) 368c2ecf20Sopenharmony_ci#define EMC6W201_REG_TEMP_HIGH(nr) (0x57 + (nr) * 2) 378c2ecf20Sopenharmony_ci#define EMC6W201_REG_FAN_MIN(nr) (0x62 + (nr) * 2) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cienum subfeature { input, min, max }; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * Per-device data 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct emc6w201_data { 468c2ecf20Sopenharmony_ci struct i2c_client *client; 478c2ecf20Sopenharmony_ci struct mutex update_lock; 488c2ecf20Sopenharmony_ci char valid; /* zero until following fields are valid */ 498c2ecf20Sopenharmony_ci unsigned long last_updated; /* in jiffies */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* registers values */ 528c2ecf20Sopenharmony_ci u8 in[3][6]; 538c2ecf20Sopenharmony_ci s8 temp[3][6]; 548c2ecf20Sopenharmony_ci u16 fan[2][5]; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * Combine LSB and MSB registers in a single value 598c2ecf20Sopenharmony_ci * Locking: must be called with data->update_lock held 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistatic u16 emc6w201_read16(struct i2c_client *client, u8 reg) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci int lsb, msb; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci lsb = i2c_smbus_read_byte_data(client, reg); 668c2ecf20Sopenharmony_ci msb = i2c_smbus_read_byte_data(client, reg + 1); 678c2ecf20Sopenharmony_ci if (unlikely(lsb < 0 || msb < 0)) { 688c2ecf20Sopenharmony_ci dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", 698c2ecf20Sopenharmony_ci 16, "read", reg); 708c2ecf20Sopenharmony_ci return 0xFFFF; /* Arbitrary value */ 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return (msb << 8) | lsb; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * Write 16-bit value to LSB and MSB registers 788c2ecf20Sopenharmony_ci * Locking: must be called with data->update_lock held 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic int emc6w201_write16(struct i2c_client *client, u8 reg, u16 val) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci int err; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci err = i2c_smbus_write_byte_data(client, reg, val & 0xff); 858c2ecf20Sopenharmony_ci if (likely(!err)) 868c2ecf20Sopenharmony_ci err = i2c_smbus_write_byte_data(client, reg + 1, val >> 8); 878c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 888c2ecf20Sopenharmony_ci dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", 898c2ecf20Sopenharmony_ci 16, "write", reg); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return err; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* Read 8-bit value from register */ 958c2ecf20Sopenharmony_cistatic u8 emc6w201_read8(struct i2c_client *client, u8 reg) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int val; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci val = i2c_smbus_read_byte_data(client, reg); 1008c2ecf20Sopenharmony_ci if (unlikely(val < 0)) { 1018c2ecf20Sopenharmony_ci dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", 1028c2ecf20Sopenharmony_ci 8, "read", reg); 1038c2ecf20Sopenharmony_ci return 0x00; /* Arbitrary value */ 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return val; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* Write 8-bit value to register */ 1108c2ecf20Sopenharmony_cistatic int emc6w201_write8(struct i2c_client *client, u8 reg, u8 val) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int err; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci err = i2c_smbus_write_byte_data(client, reg, val); 1158c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 1168c2ecf20Sopenharmony_ci dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", 1178c2ecf20Sopenharmony_ci 8, "write", reg); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return err; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic struct emc6w201_data *emc6w201_update_device(struct device *dev) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct emc6w201_data *data = dev_get_drvdata(dev); 1258c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1268c2ecf20Sopenharmony_ci int nr; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { 1318c2ecf20Sopenharmony_ci for (nr = 0; nr < 6; nr++) { 1328c2ecf20Sopenharmony_ci data->in[input][nr] = 1338c2ecf20Sopenharmony_ci emc6w201_read8(client, 1348c2ecf20Sopenharmony_ci EMC6W201_REG_IN(nr)); 1358c2ecf20Sopenharmony_ci data->in[min][nr] = 1368c2ecf20Sopenharmony_ci emc6w201_read8(client, 1378c2ecf20Sopenharmony_ci EMC6W201_REG_IN_LOW(nr)); 1388c2ecf20Sopenharmony_ci data->in[max][nr] = 1398c2ecf20Sopenharmony_ci emc6w201_read8(client, 1408c2ecf20Sopenharmony_ci EMC6W201_REG_IN_HIGH(nr)); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci for (nr = 0; nr < 6; nr++) { 1448c2ecf20Sopenharmony_ci data->temp[input][nr] = 1458c2ecf20Sopenharmony_ci emc6w201_read8(client, 1468c2ecf20Sopenharmony_ci EMC6W201_REG_TEMP(nr)); 1478c2ecf20Sopenharmony_ci data->temp[min][nr] = 1488c2ecf20Sopenharmony_ci emc6w201_read8(client, 1498c2ecf20Sopenharmony_ci EMC6W201_REG_TEMP_LOW(nr)); 1508c2ecf20Sopenharmony_ci data->temp[max][nr] = 1518c2ecf20Sopenharmony_ci emc6w201_read8(client, 1528c2ecf20Sopenharmony_ci EMC6W201_REG_TEMP_HIGH(nr)); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci for (nr = 0; nr < 5; nr++) { 1568c2ecf20Sopenharmony_ci data->fan[input][nr] = 1578c2ecf20Sopenharmony_ci emc6w201_read16(client, 1588c2ecf20Sopenharmony_ci EMC6W201_REG_FAN(nr)); 1598c2ecf20Sopenharmony_ci data->fan[min][nr] = 1608c2ecf20Sopenharmony_ci emc6w201_read16(client, 1618c2ecf20Sopenharmony_ci EMC6W201_REG_FAN_MIN(nr)); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci data->last_updated = jiffies; 1658c2ecf20Sopenharmony_ci data->valid = 1; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return data; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* 1748c2ecf20Sopenharmony_ci * Sysfs callback functions 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const s16 nominal_mv[6] = { 2500, 1500, 3300, 5000, 1500, 1500 }; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic ssize_t in_show(struct device *dev, struct device_attribute *devattr, 1808c2ecf20Sopenharmony_ci char *buf) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct emc6w201_data *data = emc6w201_update_device(dev); 1838c2ecf20Sopenharmony_ci int sf = to_sensor_dev_attr_2(devattr)->index; 1848c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(devattr)->nr; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 1878c2ecf20Sopenharmony_ci (unsigned)data->in[sf][nr] * nominal_mv[nr] / 0xC0); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic ssize_t in_store(struct device *dev, struct device_attribute *devattr, 1918c2ecf20Sopenharmony_ci const char *buf, size_t count) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct emc6w201_data *data = dev_get_drvdata(dev); 1948c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1958c2ecf20Sopenharmony_ci int sf = to_sensor_dev_attr_2(devattr)->index; 1968c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(devattr)->nr; 1978c2ecf20Sopenharmony_ci int err; 1988c2ecf20Sopenharmony_ci long val; 1998c2ecf20Sopenharmony_ci u8 reg; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &val); 2028c2ecf20Sopenharmony_ci if (err < 0) 2038c2ecf20Sopenharmony_ci return err; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci val = clamp_val(val, 0, 255 * nominal_mv[nr] / 192); 2068c2ecf20Sopenharmony_ci val = DIV_ROUND_CLOSEST(val * 192, nominal_mv[nr]); 2078c2ecf20Sopenharmony_ci reg = (sf == min) ? EMC6W201_REG_IN_LOW(nr) 2088c2ecf20Sopenharmony_ci : EMC6W201_REG_IN_HIGH(nr); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2118c2ecf20Sopenharmony_ci data->in[sf][nr] = val; 2128c2ecf20Sopenharmony_ci err = emc6w201_write8(client, reg, data->in[sf][nr]); 2138c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return err < 0 ? err : count; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic ssize_t temp_show(struct device *dev, struct device_attribute *devattr, 2198c2ecf20Sopenharmony_ci char *buf) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct emc6w201_data *data = emc6w201_update_device(dev); 2228c2ecf20Sopenharmony_ci int sf = to_sensor_dev_attr_2(devattr)->index; 2238c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(devattr)->nr; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (int)data->temp[sf][nr] * 1000); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic ssize_t temp_store(struct device *dev, 2298c2ecf20Sopenharmony_ci struct device_attribute *devattr, const char *buf, 2308c2ecf20Sopenharmony_ci size_t count) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct emc6w201_data *data = dev_get_drvdata(dev); 2338c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 2348c2ecf20Sopenharmony_ci int sf = to_sensor_dev_attr_2(devattr)->index; 2358c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(devattr)->nr; 2368c2ecf20Sopenharmony_ci int err; 2378c2ecf20Sopenharmony_ci long val; 2388c2ecf20Sopenharmony_ci u8 reg; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci err = kstrtol(buf, 10, &val); 2418c2ecf20Sopenharmony_ci if (err < 0) 2428c2ecf20Sopenharmony_ci return err; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci val = clamp_val(val, -127000, 127000); 2458c2ecf20Sopenharmony_ci val = DIV_ROUND_CLOSEST(val, 1000); 2468c2ecf20Sopenharmony_ci reg = (sf == min) ? EMC6W201_REG_TEMP_LOW(nr) 2478c2ecf20Sopenharmony_ci : EMC6W201_REG_TEMP_HIGH(nr); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2508c2ecf20Sopenharmony_ci data->temp[sf][nr] = val; 2518c2ecf20Sopenharmony_ci err = emc6w201_write8(client, reg, data->temp[sf][nr]); 2528c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return err < 0 ? err : count; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic ssize_t fan_show(struct device *dev, struct device_attribute *devattr, 2588c2ecf20Sopenharmony_ci char *buf) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct emc6w201_data *data = emc6w201_update_device(dev); 2618c2ecf20Sopenharmony_ci int sf = to_sensor_dev_attr_2(devattr)->index; 2628c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(devattr)->nr; 2638c2ecf20Sopenharmony_ci unsigned rpm; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (data->fan[sf][nr] == 0 || data->fan[sf][nr] == 0xFFFF) 2668c2ecf20Sopenharmony_ci rpm = 0; 2678c2ecf20Sopenharmony_ci else 2688c2ecf20Sopenharmony_ci rpm = 5400000U / data->fan[sf][nr]; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", rpm); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic ssize_t fan_store(struct device *dev, struct device_attribute *devattr, 2748c2ecf20Sopenharmony_ci const char *buf, size_t count) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct emc6w201_data *data = dev_get_drvdata(dev); 2778c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 2788c2ecf20Sopenharmony_ci int sf = to_sensor_dev_attr_2(devattr)->index; 2798c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(devattr)->nr; 2808c2ecf20Sopenharmony_ci int err; 2818c2ecf20Sopenharmony_ci unsigned long val; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 2848c2ecf20Sopenharmony_ci if (err < 0) 2858c2ecf20Sopenharmony_ci return err; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (val == 0) { 2888c2ecf20Sopenharmony_ci val = 0xFFFF; 2898c2ecf20Sopenharmony_ci } else { 2908c2ecf20Sopenharmony_ci val = DIV_ROUND_CLOSEST(5400000U, val); 2918c2ecf20Sopenharmony_ci val = clamp_val(val, 0, 0xFFFE); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2958c2ecf20Sopenharmony_ci data->fan[sf][nr] = val; 2968c2ecf20Sopenharmony_ci err = emc6w201_write16(client, EMC6W201_REG_FAN_MIN(nr), 2978c2ecf20Sopenharmony_ci data->fan[sf][nr]); 2988c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return err < 0 ? err : count; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in0_input, in, 0, input); 3048c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in0_min, in, 0, min); 3058c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in0_max, in, 0, max); 3068c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in1_input, in, 1, input); 3078c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in1_min, in, 1, min); 3088c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in1_max, in, 1, max); 3098c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in2_input, in, 2, input); 3108c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in2_min, in, 2, min); 3118c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in2_max, in, 2, max); 3128c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in3_input, in, 3, input); 3138c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in3_min, in, 3, min); 3148c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in3_max, in, 3, max); 3158c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in4_input, in, 4, input); 3168c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in4_min, in, 4, min); 3178c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in4_max, in, 4, max); 3188c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in5_input, in, 5, input); 3198c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in5_min, in, 5, min); 3208c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in5_max, in, 5, max); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, 0, input); 3238c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp1_min, temp, 0, min); 3248c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp, 0, max); 3258c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(temp2_input, temp, 1, input); 3268c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp2_min, temp, 1, min); 3278c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp, 1, max); 3288c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(temp3_input, temp, 2, input); 3298c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp3_min, temp, 2, min); 3308c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp3_max, temp, 2, max); 3318c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(temp4_input, temp, 3, input); 3328c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp4_min, temp, 3, min); 3338c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp4_max, temp, 3, max); 3348c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(temp5_input, temp, 4, input); 3358c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp5_min, temp, 4, min); 3368c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp5_max, temp, 4, max); 3378c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(temp6_input, temp, 5, input); 3388c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp6_min, temp, 5, min); 3398c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp6_max, temp, 5, max); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(fan1_input, fan, 0, input); 3428c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(fan1_min, fan, 0, min); 3438c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(fan2_input, fan, 1, input); 3448c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(fan2_min, fan, 1, min); 3458c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(fan3_input, fan, 2, input); 3468c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(fan3_min, fan, 2, min); 3478c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(fan4_input, fan, 3, input); 3488c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(fan4_min, fan, 3, min); 3498c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(fan5_input, fan, 4, input); 3508c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(fan5_min, fan, 4, min); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic struct attribute *emc6w201_attrs[] = { 3538c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_input.dev_attr.attr, 3548c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_min.dev_attr.attr, 3558c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_max.dev_attr.attr, 3568c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_input.dev_attr.attr, 3578c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_min.dev_attr.attr, 3588c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_max.dev_attr.attr, 3598c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_input.dev_attr.attr, 3608c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_min.dev_attr.attr, 3618c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_max.dev_attr.attr, 3628c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_input.dev_attr.attr, 3638c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_min.dev_attr.attr, 3648c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_max.dev_attr.attr, 3658c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_input.dev_attr.attr, 3668c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_min.dev_attr.attr, 3678c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_max.dev_attr.attr, 3688c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_input.dev_attr.attr, 3698c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_min.dev_attr.attr, 3708c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_max.dev_attr.attr, 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 3738c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_min.dev_attr.attr, 3748c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max.dev_attr.attr, 3758c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_input.dev_attr.attr, 3768c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_min.dev_attr.attr, 3778c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_max.dev_attr.attr, 3788c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_input.dev_attr.attr, 3798c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_min.dev_attr.attr, 3808c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_max.dev_attr.attr, 3818c2ecf20Sopenharmony_ci &sensor_dev_attr_temp4_input.dev_attr.attr, 3828c2ecf20Sopenharmony_ci &sensor_dev_attr_temp4_min.dev_attr.attr, 3838c2ecf20Sopenharmony_ci &sensor_dev_attr_temp4_max.dev_attr.attr, 3848c2ecf20Sopenharmony_ci &sensor_dev_attr_temp5_input.dev_attr.attr, 3858c2ecf20Sopenharmony_ci &sensor_dev_attr_temp5_min.dev_attr.attr, 3868c2ecf20Sopenharmony_ci &sensor_dev_attr_temp5_max.dev_attr.attr, 3878c2ecf20Sopenharmony_ci &sensor_dev_attr_temp6_input.dev_attr.attr, 3888c2ecf20Sopenharmony_ci &sensor_dev_attr_temp6_min.dev_attr.attr, 3898c2ecf20Sopenharmony_ci &sensor_dev_attr_temp6_max.dev_attr.attr, 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_input.dev_attr.attr, 3928c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_min.dev_attr.attr, 3938c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_input.dev_attr.attr, 3948c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_min.dev_attr.attr, 3958c2ecf20Sopenharmony_ci &sensor_dev_attr_fan3_input.dev_attr.attr, 3968c2ecf20Sopenharmony_ci &sensor_dev_attr_fan3_min.dev_attr.attr, 3978c2ecf20Sopenharmony_ci &sensor_dev_attr_fan4_input.dev_attr.attr, 3988c2ecf20Sopenharmony_ci &sensor_dev_attr_fan4_min.dev_attr.attr, 3998c2ecf20Sopenharmony_ci &sensor_dev_attr_fan5_input.dev_attr.attr, 4008c2ecf20Sopenharmony_ci &sensor_dev_attr_fan5_min.dev_attr.attr, 4018c2ecf20Sopenharmony_ci NULL 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(emc6w201); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci/* 4078c2ecf20Sopenharmony_ci * Driver interface 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */ 4118c2ecf20Sopenharmony_cistatic int emc6w201_detect(struct i2c_client *client, 4128c2ecf20Sopenharmony_ci struct i2c_board_info *info) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 4158c2ecf20Sopenharmony_ci int company, verstep, config; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 4188c2ecf20Sopenharmony_ci return -ENODEV; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* Identification */ 4218c2ecf20Sopenharmony_ci company = i2c_smbus_read_byte_data(client, EMC6W201_REG_COMPANY); 4228c2ecf20Sopenharmony_ci if (company != 0x5C) 4238c2ecf20Sopenharmony_ci return -ENODEV; 4248c2ecf20Sopenharmony_ci verstep = i2c_smbus_read_byte_data(client, EMC6W201_REG_VERSTEP); 4258c2ecf20Sopenharmony_ci if (verstep < 0 || (verstep & 0xF0) != 0xB0) 4268c2ecf20Sopenharmony_ci return -ENODEV; 4278c2ecf20Sopenharmony_ci if ((verstep & 0x0F) > 2) { 4288c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "Unknown EMC6W201 stepping %d\n", 4298c2ecf20Sopenharmony_ci verstep & 0x0F); 4308c2ecf20Sopenharmony_ci return -ENODEV; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* Check configuration */ 4348c2ecf20Sopenharmony_ci config = i2c_smbus_read_byte_data(client, EMC6W201_REG_CONFIG); 4358c2ecf20Sopenharmony_ci if (config < 0 || (config & 0xF4) != 0x04) 4368c2ecf20Sopenharmony_ci return -ENODEV; 4378c2ecf20Sopenharmony_ci if (!(config & 0x01)) { 4388c2ecf20Sopenharmony_ci dev_err(&client->dev, "Monitoring not enabled\n"); 4398c2ecf20Sopenharmony_ci return -ENODEV; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci strlcpy(info->type, "emc6w201", I2C_NAME_SIZE); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int emc6w201_probe(struct i2c_client *client) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 4508c2ecf20Sopenharmony_ci struct emc6w201_data *data; 4518c2ecf20Sopenharmony_ci struct device *hwmon_dev; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct emc6w201_data), GFP_KERNEL); 4548c2ecf20Sopenharmony_ci if (!data) 4558c2ecf20Sopenharmony_ci return -ENOMEM; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci data->client = client; 4588c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 4618c2ecf20Sopenharmony_ci data, 4628c2ecf20Sopenharmony_ci emc6w201_groups); 4638c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic const struct i2c_device_id emc6w201_id[] = { 4678c2ecf20Sopenharmony_ci { "emc6w201", 0 }, 4688c2ecf20Sopenharmony_ci { } 4698c2ecf20Sopenharmony_ci}; 4708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, emc6w201_id); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic struct i2c_driver emc6w201_driver = { 4738c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 4748c2ecf20Sopenharmony_ci .driver = { 4758c2ecf20Sopenharmony_ci .name = "emc6w201", 4768c2ecf20Sopenharmony_ci }, 4778c2ecf20Sopenharmony_ci .probe_new = emc6w201_probe, 4788c2ecf20Sopenharmony_ci .id_table = emc6w201_id, 4798c2ecf20Sopenharmony_ci .detect = emc6w201_detect, 4808c2ecf20Sopenharmony_ci .address_list = normal_i2c, 4818c2ecf20Sopenharmony_ci}; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cimodule_i2c_driver(emc6w201_driver); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); 4868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SMSC EMC6W201 hardware monitoring driver"); 4878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 488