18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * lm80.c - From lm_sensors, Linux kernel modules for hardware 48c2ecf20Sopenharmony_ci * monitoring 58c2ecf20Sopenharmony_ci * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> 68c2ecf20Sopenharmony_ci * and Philip Edelbrock <phil@netroedge.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Ported to Linux 2.6 by Tiago Sousa <mirage@kaotik.org> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 178c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/mutex.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* Addresses to scan */ 228c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 238c2ecf20Sopenharmony_ci 0x2e, 0x2f, I2C_CLIENT_END }; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Many LM80 constants specified below */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* The LM80 registers */ 288c2ecf20Sopenharmony_ci#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2) 298c2ecf20Sopenharmony_ci#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2) 308c2ecf20Sopenharmony_ci#define LM80_REG_IN(nr) (0x20 + (nr)) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define LM80_REG_FAN1 0x28 338c2ecf20Sopenharmony_ci#define LM80_REG_FAN2 0x29 348c2ecf20Sopenharmony_ci#define LM80_REG_FAN_MIN(nr) (0x3b + (nr)) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define LM80_REG_TEMP 0x27 378c2ecf20Sopenharmony_ci#define LM80_REG_TEMP_HOT_MAX 0x38 388c2ecf20Sopenharmony_ci#define LM80_REG_TEMP_HOT_HYST 0x39 398c2ecf20Sopenharmony_ci#define LM80_REG_TEMP_OS_MAX 0x3a 408c2ecf20Sopenharmony_ci#define LM80_REG_TEMP_OS_HYST 0x3b 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define LM80_REG_CONFIG 0x00 438c2ecf20Sopenharmony_ci#define LM80_REG_ALARM1 0x01 448c2ecf20Sopenharmony_ci#define LM80_REG_ALARM2 0x02 458c2ecf20Sopenharmony_ci#define LM80_REG_MASK1 0x03 468c2ecf20Sopenharmony_ci#define LM80_REG_MASK2 0x04 478c2ecf20Sopenharmony_ci#define LM80_REG_FANDIV 0x05 488c2ecf20Sopenharmony_ci#define LM80_REG_RES 0x06 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define LM96080_REG_CONV_RATE 0x07 518c2ecf20Sopenharmony_ci#define LM96080_REG_MAN_ID 0x3e 528c2ecf20Sopenharmony_ci#define LM96080_REG_DEV_ID 0x3f 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * Conversions. Rounding and limit checking is only done on the TO_REG 578c2ecf20Sopenharmony_ci * variants. Note that you should be a bit careful with which arguments 588c2ecf20Sopenharmony_ci * these macros are called: arguments may be evaluated more than once. 598c2ecf20Sopenharmony_ci * Fixing this is just not worth it. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define IN_TO_REG(val) (clamp_val(((val) + 5) / 10, 0, 255)) 638c2ecf20Sopenharmony_ci#define IN_FROM_REG(val) ((val) * 10) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci if (rpm == 0) 688c2ecf20Sopenharmony_ci return 255; 698c2ecf20Sopenharmony_ci rpm = clamp_val(rpm, 1, 1000000); 708c2ecf20Sopenharmony_ci return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \ 748c2ecf20Sopenharmony_ci (val) == 255 ? 0 : 1350000/((div) * (val))) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define TEMP_FROM_REG(reg) ((reg) * 125 / 32) 778c2ecf20Sopenharmony_ci#define TEMP_TO_REG(temp) (DIV_ROUND_CLOSEST(clamp_val((temp), \ 788c2ecf20Sopenharmony_ci -128000, 127000), 1000) << 8) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define DIV_FROM_REG(val) (1 << (val)) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cienum temp_index { 838c2ecf20Sopenharmony_ci t_input = 0, 848c2ecf20Sopenharmony_ci t_hot_max, 858c2ecf20Sopenharmony_ci t_hot_hyst, 868c2ecf20Sopenharmony_ci t_os_max, 878c2ecf20Sopenharmony_ci t_os_hyst, 888c2ecf20Sopenharmony_ci t_num_temp 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic const u8 temp_regs[t_num_temp] = { 928c2ecf20Sopenharmony_ci [t_input] = LM80_REG_TEMP, 938c2ecf20Sopenharmony_ci [t_hot_max] = LM80_REG_TEMP_HOT_MAX, 948c2ecf20Sopenharmony_ci [t_hot_hyst] = LM80_REG_TEMP_HOT_HYST, 958c2ecf20Sopenharmony_ci [t_os_max] = LM80_REG_TEMP_OS_MAX, 968c2ecf20Sopenharmony_ci [t_os_hyst] = LM80_REG_TEMP_OS_HYST, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cienum in_index { 1008c2ecf20Sopenharmony_ci i_input = 0, 1018c2ecf20Sopenharmony_ci i_max, 1028c2ecf20Sopenharmony_ci i_min, 1038c2ecf20Sopenharmony_ci i_num_in 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cienum fan_index { 1078c2ecf20Sopenharmony_ci f_input, 1088c2ecf20Sopenharmony_ci f_min, 1098c2ecf20Sopenharmony_ci f_num_fan 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* 1138c2ecf20Sopenharmony_ci * Client data (each client gets its own) 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistruct lm80_data { 1178c2ecf20Sopenharmony_ci struct i2c_client *client; 1188c2ecf20Sopenharmony_ci struct mutex update_lock; 1198c2ecf20Sopenharmony_ci char error; /* !=0 if error occurred during last update */ 1208c2ecf20Sopenharmony_ci char valid; /* !=0 if following fields are valid */ 1218c2ecf20Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci u8 in[i_num_in][7]; /* Register value, 1st index is enum in_index */ 1248c2ecf20Sopenharmony_ci u8 fan[f_num_fan][2]; /* Register value, 1st index enum fan_index */ 1258c2ecf20Sopenharmony_ci u8 fan_div[2]; /* Register encoding, shifted right */ 1268c2ecf20Sopenharmony_ci s16 temp[t_num_temp]; /* Register values, normalized to 16 bit */ 1278c2ecf20Sopenharmony_ci u16 alarms; /* Register encoding, combined */ 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int lm80_read_value(struct i2c_client *client, u8 reg) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci return i2c_smbus_read_byte_data(client, reg); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int lm80_write_value(struct i2c_client *client, u8 reg, u8 value) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(client, reg, value); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* Called when we have found a new LM80 and after read errors */ 1418c2ecf20Sopenharmony_cistatic void lm80_init_client(struct i2c_client *client) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci /* 1448c2ecf20Sopenharmony_ci * Reset all except Watchdog values and last conversion values 1458c2ecf20Sopenharmony_ci * This sets fan-divs to 2, among others. This makes most other 1468c2ecf20Sopenharmony_ci * initializations unnecessary 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci lm80_write_value(client, LM80_REG_CONFIG, 0x80); 1498c2ecf20Sopenharmony_ci /* Set 11-bit temperature resolution */ 1508c2ecf20Sopenharmony_ci lm80_write_value(client, LM80_REG_RES, 0x08); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Start monitoring */ 1538c2ecf20Sopenharmony_ci lm80_write_value(client, LM80_REG_CONFIG, 0x01); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic struct lm80_data *lm80_update_device(struct device *dev) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct lm80_data *data = dev_get_drvdata(dev); 1598c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1608c2ecf20Sopenharmony_ci int i; 1618c2ecf20Sopenharmony_ci int rv; 1628c2ecf20Sopenharmony_ci int prev_rv; 1638c2ecf20Sopenharmony_ci struct lm80_data *ret = data; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (data->error) 1688c2ecf20Sopenharmony_ci lm80_init_client(client); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { 1718c2ecf20Sopenharmony_ci dev_dbg(dev, "Starting lm80 update\n"); 1728c2ecf20Sopenharmony_ci for (i = 0; i <= 6; i++) { 1738c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_IN(i)); 1748c2ecf20Sopenharmony_ci if (rv < 0) 1758c2ecf20Sopenharmony_ci goto abort; 1768c2ecf20Sopenharmony_ci data->in[i_input][i] = rv; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_IN_MIN(i)); 1798c2ecf20Sopenharmony_ci if (rv < 0) 1808c2ecf20Sopenharmony_ci goto abort; 1818c2ecf20Sopenharmony_ci data->in[i_min][i] = rv; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_IN_MAX(i)); 1848c2ecf20Sopenharmony_ci if (rv < 0) 1858c2ecf20Sopenharmony_ci goto abort; 1868c2ecf20Sopenharmony_ci data->in[i_max][i] = rv; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_FAN1); 1908c2ecf20Sopenharmony_ci if (rv < 0) 1918c2ecf20Sopenharmony_ci goto abort; 1928c2ecf20Sopenharmony_ci data->fan[f_input][0] = rv; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_FAN_MIN(1)); 1958c2ecf20Sopenharmony_ci if (rv < 0) 1968c2ecf20Sopenharmony_ci goto abort; 1978c2ecf20Sopenharmony_ci data->fan[f_min][0] = rv; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_FAN2); 2008c2ecf20Sopenharmony_ci if (rv < 0) 2018c2ecf20Sopenharmony_ci goto abort; 2028c2ecf20Sopenharmony_ci data->fan[f_input][1] = rv; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_FAN_MIN(2)); 2058c2ecf20Sopenharmony_ci if (rv < 0) 2068c2ecf20Sopenharmony_ci goto abort; 2078c2ecf20Sopenharmony_ci data->fan[f_min][1] = rv; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci prev_rv = rv = lm80_read_value(client, LM80_REG_TEMP); 2108c2ecf20Sopenharmony_ci if (rv < 0) 2118c2ecf20Sopenharmony_ci goto abort; 2128c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_RES); 2138c2ecf20Sopenharmony_ci if (rv < 0) 2148c2ecf20Sopenharmony_ci goto abort; 2158c2ecf20Sopenharmony_ci data->temp[t_input] = (prev_rv << 8) | (rv & 0xf0); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci for (i = t_input + 1; i < t_num_temp; i++) { 2188c2ecf20Sopenharmony_ci rv = lm80_read_value(client, temp_regs[i]); 2198c2ecf20Sopenharmony_ci if (rv < 0) 2208c2ecf20Sopenharmony_ci goto abort; 2218c2ecf20Sopenharmony_ci data->temp[i] = rv << 8; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_FANDIV); 2258c2ecf20Sopenharmony_ci if (rv < 0) 2268c2ecf20Sopenharmony_ci goto abort; 2278c2ecf20Sopenharmony_ci data->fan_div[0] = (rv >> 2) & 0x03; 2288c2ecf20Sopenharmony_ci data->fan_div[1] = (rv >> 4) & 0x03; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci prev_rv = rv = lm80_read_value(client, LM80_REG_ALARM1); 2318c2ecf20Sopenharmony_ci if (rv < 0) 2328c2ecf20Sopenharmony_ci goto abort; 2338c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_ALARM2); 2348c2ecf20Sopenharmony_ci if (rv < 0) 2358c2ecf20Sopenharmony_ci goto abort; 2368c2ecf20Sopenharmony_ci data->alarms = prev_rv + (rv << 8); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci data->last_updated = jiffies; 2398c2ecf20Sopenharmony_ci data->valid = 1; 2408c2ecf20Sopenharmony_ci data->error = 0; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci goto done; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ciabort: 2458c2ecf20Sopenharmony_ci ret = ERR_PTR(rv); 2468c2ecf20Sopenharmony_ci data->valid = 0; 2478c2ecf20Sopenharmony_ci data->error = 1; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cidone: 2508c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return ret; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* 2568c2ecf20Sopenharmony_ci * Sysfs stuff 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic ssize_t in_show(struct device *dev, struct device_attribute *attr, 2608c2ecf20Sopenharmony_ci char *buf) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct lm80_data *data = lm80_update_device(dev); 2638c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr_2(attr)->index; 2648c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(attr)->nr; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (IS_ERR(data)) 2678c2ecf20Sopenharmony_ci return PTR_ERR(data); 2688c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr][index])); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic ssize_t in_store(struct device *dev, struct device_attribute *attr, 2728c2ecf20Sopenharmony_ci const char *buf, size_t count) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct lm80_data *data = dev_get_drvdata(dev); 2758c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 2768c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr_2(attr)->index; 2778c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(attr)->nr; 2788c2ecf20Sopenharmony_ci long val; 2798c2ecf20Sopenharmony_ci u8 reg; 2808c2ecf20Sopenharmony_ci int err = kstrtol(buf, 10, &val); 2818c2ecf20Sopenharmony_ci if (err < 0) 2828c2ecf20Sopenharmony_ci return err; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci reg = nr == i_min ? LM80_REG_IN_MIN(index) : LM80_REG_IN_MAX(index); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2878c2ecf20Sopenharmony_ci data->in[nr][index] = IN_TO_REG(val); 2888c2ecf20Sopenharmony_ci lm80_write_value(client, reg, data->in[nr][index]); 2898c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2908c2ecf20Sopenharmony_ci return count; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic ssize_t fan_show(struct device *dev, struct device_attribute *attr, 2948c2ecf20Sopenharmony_ci char *buf) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr_2(attr)->index; 2978c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(attr)->nr; 2988c2ecf20Sopenharmony_ci struct lm80_data *data = lm80_update_device(dev); 2998c2ecf20Sopenharmony_ci if (IS_ERR(data)) 3008c2ecf20Sopenharmony_ci return PTR_ERR(data); 3018c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr][index], 3028c2ecf20Sopenharmony_ci DIV_FROM_REG(data->fan_div[index]))); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic ssize_t fan_div_show(struct device *dev, struct device_attribute *attr, 3068c2ecf20Sopenharmony_ci char *buf) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 3098c2ecf20Sopenharmony_ci struct lm80_data *data = lm80_update_device(dev); 3108c2ecf20Sopenharmony_ci if (IS_ERR(data)) 3118c2ecf20Sopenharmony_ci return PTR_ERR(data); 3128c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic ssize_t fan_store(struct device *dev, struct device_attribute *attr, 3168c2ecf20Sopenharmony_ci const char *buf, size_t count) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci int index = to_sensor_dev_attr_2(attr)->index; 3198c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(attr)->nr; 3208c2ecf20Sopenharmony_ci struct lm80_data *data = dev_get_drvdata(dev); 3218c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 3228c2ecf20Sopenharmony_ci unsigned long val; 3238c2ecf20Sopenharmony_ci int err = kstrtoul(buf, 10, &val); 3248c2ecf20Sopenharmony_ci if (err < 0) 3258c2ecf20Sopenharmony_ci return err; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3288c2ecf20Sopenharmony_ci data->fan[nr][index] = FAN_TO_REG(val, 3298c2ecf20Sopenharmony_ci DIV_FROM_REG(data->fan_div[index])); 3308c2ecf20Sopenharmony_ci lm80_write_value(client, LM80_REG_FAN_MIN(index + 1), 3318c2ecf20Sopenharmony_ci data->fan[nr][index]); 3328c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3338c2ecf20Sopenharmony_ci return count; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* 3378c2ecf20Sopenharmony_ci * Note: we save and restore the fan minimum here, because its value is 3388c2ecf20Sopenharmony_ci * determined in part by the fan divisor. This follows the principle of 3398c2ecf20Sopenharmony_ci * least surprise; the user doesn't expect the fan minimum to change just 3408c2ecf20Sopenharmony_ci * because the divisor changed. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_cistatic ssize_t fan_div_store(struct device *dev, 3438c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 3448c2ecf20Sopenharmony_ci size_t count) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 3478c2ecf20Sopenharmony_ci struct lm80_data *data = dev_get_drvdata(dev); 3488c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 3498c2ecf20Sopenharmony_ci unsigned long min, val; 3508c2ecf20Sopenharmony_ci u8 reg; 3518c2ecf20Sopenharmony_ci int rv; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci rv = kstrtoul(buf, 10, &val); 3548c2ecf20Sopenharmony_ci if (rv < 0) 3558c2ecf20Sopenharmony_ci return rv; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* Save fan_min */ 3588c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3598c2ecf20Sopenharmony_ci min = FAN_FROM_REG(data->fan[f_min][nr], 3608c2ecf20Sopenharmony_ci DIV_FROM_REG(data->fan_div[nr])); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci switch (val) { 3638c2ecf20Sopenharmony_ci case 1: 3648c2ecf20Sopenharmony_ci data->fan_div[nr] = 0; 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci case 2: 3678c2ecf20Sopenharmony_ci data->fan_div[nr] = 1; 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci case 4: 3708c2ecf20Sopenharmony_ci data->fan_div[nr] = 2; 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci case 8: 3738c2ecf20Sopenharmony_ci data->fan_div[nr] = 3; 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci default: 3768c2ecf20Sopenharmony_ci dev_err(dev, 3778c2ecf20Sopenharmony_ci "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", 3788c2ecf20Sopenharmony_ci val); 3798c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3808c2ecf20Sopenharmony_ci return -EINVAL; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci rv = lm80_read_value(client, LM80_REG_FANDIV); 3848c2ecf20Sopenharmony_ci if (rv < 0) { 3858c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3868c2ecf20Sopenharmony_ci return rv; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci reg = (rv & ~(3 << (2 * (nr + 1)))) 3898c2ecf20Sopenharmony_ci | (data->fan_div[nr] << (2 * (nr + 1))); 3908c2ecf20Sopenharmony_ci lm80_write_value(client, LM80_REG_FANDIV, reg); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* Restore fan_min */ 3938c2ecf20Sopenharmony_ci data->fan[f_min][nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); 3948c2ecf20Sopenharmony_ci lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), 3958c2ecf20Sopenharmony_ci data->fan[f_min][nr]); 3968c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return count; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic ssize_t temp_show(struct device *dev, struct device_attribute *devattr, 4028c2ecf20Sopenharmony_ci char *buf) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 4058c2ecf20Sopenharmony_ci struct lm80_data *data = lm80_update_device(dev); 4068c2ecf20Sopenharmony_ci if (IS_ERR(data)) 4078c2ecf20Sopenharmony_ci return PTR_ERR(data); 4088c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic ssize_t temp_store(struct device *dev, 4128c2ecf20Sopenharmony_ci struct device_attribute *devattr, const char *buf, 4138c2ecf20Sopenharmony_ci size_t count) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 4168c2ecf20Sopenharmony_ci struct lm80_data *data = dev_get_drvdata(dev); 4178c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 4188c2ecf20Sopenharmony_ci int nr = attr->index; 4198c2ecf20Sopenharmony_ci long val; 4208c2ecf20Sopenharmony_ci int err = kstrtol(buf, 10, &val); 4218c2ecf20Sopenharmony_ci if (err < 0) 4228c2ecf20Sopenharmony_ci return err; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 4258c2ecf20Sopenharmony_ci data->temp[nr] = TEMP_TO_REG(val); 4268c2ecf20Sopenharmony_ci lm80_write_value(client, temp_regs[nr], data->temp[nr] >> 8); 4278c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4288c2ecf20Sopenharmony_ci return count; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic ssize_t alarms_show(struct device *dev, struct device_attribute *attr, 4328c2ecf20Sopenharmony_ci char *buf) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct lm80_data *data = lm80_update_device(dev); 4358c2ecf20Sopenharmony_ci if (IS_ERR(data)) 4368c2ecf20Sopenharmony_ci return PTR_ERR(data); 4378c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", data->alarms); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic ssize_t alarm_show(struct device *dev, struct device_attribute *attr, 4418c2ecf20Sopenharmony_ci char *buf) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci int bitnr = to_sensor_dev_attr(attr)->index; 4448c2ecf20Sopenharmony_ci struct lm80_data *data = lm80_update_device(dev); 4458c2ecf20Sopenharmony_ci if (IS_ERR(data)) 4468c2ecf20Sopenharmony_ci return PTR_ERR(data); 4478c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in0_min, in, i_min, 0); 4518c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in1_min, in, i_min, 1); 4528c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in2_min, in, i_min, 2); 4538c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in3_min, in, i_min, 3); 4548c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in4_min, in, i_min, 4); 4558c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in5_min, in, i_min, 5); 4568c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in6_min, in, i_min, 6); 4578c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in0_max, in, i_max, 0); 4588c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in1_max, in, i_max, 1); 4598c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in2_max, in, i_max, 2); 4608c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in3_max, in, i_max, 3); 4618c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in4_max, in, i_max, 4); 4628c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in5_max, in, i_max, 5); 4638c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(in6_max, in, i_max, 6); 4648c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in0_input, in, i_input, 0); 4658c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in1_input, in, i_input, 1); 4668c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in2_input, in, i_input, 2); 4678c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in3_input, in, i_input, 3); 4688c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in4_input, in, i_input, 4); 4698c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in5_input, in, i_input, 5); 4708c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(in6_input, in, i_input, 6); 4718c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(fan1_min, fan, f_min, 0); 4728c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(fan2_min, fan, f_min, 1); 4738c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(fan1_input, fan, f_input, 0); 4748c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(fan2_input, fan, f_input, 1); 4758c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0); 4768c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1); 4778c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input); 4788c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_max, temp, t_hot_max); 4798c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, temp, t_hot_hyst); 4808c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, t_os_max); 4818c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, temp, t_os_hyst); 4828c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(alarms); 4838c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0); 4848c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1); 4858c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2); 4868c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 3); 4878c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 4); 4888c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 5); 4898c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in6_alarm, alarm, 6); 4908c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 10); 4918c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 11); 4928c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 8); 4938c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 13); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci/* 4968c2ecf20Sopenharmony_ci * Real code 4978c2ecf20Sopenharmony_ci */ 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic struct attribute *lm80_attrs[] = { 5008c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_min.dev_attr.attr, 5018c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_min.dev_attr.attr, 5028c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_min.dev_attr.attr, 5038c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_min.dev_attr.attr, 5048c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_min.dev_attr.attr, 5058c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_min.dev_attr.attr, 5068c2ecf20Sopenharmony_ci &sensor_dev_attr_in6_min.dev_attr.attr, 5078c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_max.dev_attr.attr, 5088c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_max.dev_attr.attr, 5098c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_max.dev_attr.attr, 5108c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_max.dev_attr.attr, 5118c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_max.dev_attr.attr, 5128c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_max.dev_attr.attr, 5138c2ecf20Sopenharmony_ci &sensor_dev_attr_in6_max.dev_attr.attr, 5148c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_input.dev_attr.attr, 5158c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_input.dev_attr.attr, 5168c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_input.dev_attr.attr, 5178c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_input.dev_attr.attr, 5188c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_input.dev_attr.attr, 5198c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_input.dev_attr.attr, 5208c2ecf20Sopenharmony_ci &sensor_dev_attr_in6_input.dev_attr.attr, 5218c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_min.dev_attr.attr, 5228c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_min.dev_attr.attr, 5238c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_input.dev_attr.attr, 5248c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_input.dev_attr.attr, 5258c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_div.dev_attr.attr, 5268c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_div.dev_attr.attr, 5278c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 5288c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max.dev_attr.attr, 5298c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, 5308c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit.dev_attr.attr, 5318c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, 5328c2ecf20Sopenharmony_ci &dev_attr_alarms.attr, 5338c2ecf20Sopenharmony_ci &sensor_dev_attr_in0_alarm.dev_attr.attr, 5348c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_alarm.dev_attr.attr, 5358c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_alarm.dev_attr.attr, 5368c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_alarm.dev_attr.attr, 5378c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_alarm.dev_attr.attr, 5388c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_alarm.dev_attr.attr, 5398c2ecf20Sopenharmony_ci &sensor_dev_attr_in6_alarm.dev_attr.attr, 5408c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_alarm.dev_attr.attr, 5418c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_alarm.dev_attr.attr, 5428c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, 5438c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, 5448c2ecf20Sopenharmony_ci NULL 5458c2ecf20Sopenharmony_ci}; 5468c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(lm80); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */ 5498c2ecf20Sopenharmony_cistatic int lm80_detect(struct i2c_client *client, struct i2c_board_info *info) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 5528c2ecf20Sopenharmony_ci int i, cur, man_id, dev_id; 5538c2ecf20Sopenharmony_ci const char *name = NULL; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 5568c2ecf20Sopenharmony_ci return -ENODEV; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* First check for unused bits, common to both chip types */ 5598c2ecf20Sopenharmony_ci if ((lm80_read_value(client, LM80_REG_ALARM2) & 0xc0) 5608c2ecf20Sopenharmony_ci || (lm80_read_value(client, LM80_REG_CONFIG) & 0x80)) 5618c2ecf20Sopenharmony_ci return -ENODEV; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci /* 5648c2ecf20Sopenharmony_ci * The LM96080 has manufacturer and stepping/die rev registers so we 5658c2ecf20Sopenharmony_ci * can just check that. The LM80 does not have such registers so we 5668c2ecf20Sopenharmony_ci * have to use a more expensive trick. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_ci man_id = lm80_read_value(client, LM96080_REG_MAN_ID); 5698c2ecf20Sopenharmony_ci dev_id = lm80_read_value(client, LM96080_REG_DEV_ID); 5708c2ecf20Sopenharmony_ci if (man_id == 0x01 && dev_id == 0x08) { 5718c2ecf20Sopenharmony_ci /* Check more unused bits for confirmation */ 5728c2ecf20Sopenharmony_ci if (lm80_read_value(client, LM96080_REG_CONV_RATE) & 0xfe) 5738c2ecf20Sopenharmony_ci return -ENODEV; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci name = "lm96080"; 5768c2ecf20Sopenharmony_ci } else { 5778c2ecf20Sopenharmony_ci /* Check 6-bit addressing */ 5788c2ecf20Sopenharmony_ci for (i = 0x2a; i <= 0x3d; i++) { 5798c2ecf20Sopenharmony_ci cur = i2c_smbus_read_byte_data(client, i); 5808c2ecf20Sopenharmony_ci if ((i2c_smbus_read_byte_data(client, i + 0x40) != cur) 5818c2ecf20Sopenharmony_ci || (i2c_smbus_read_byte_data(client, i + 0x80) != cur) 5828c2ecf20Sopenharmony_ci || (i2c_smbus_read_byte_data(client, i + 0xc0) != cur)) 5838c2ecf20Sopenharmony_ci return -ENODEV; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci name = "lm80"; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci strlcpy(info->type, name, I2C_NAME_SIZE); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic int lm80_probe(struct i2c_client *client) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 5978c2ecf20Sopenharmony_ci struct device *hwmon_dev; 5988c2ecf20Sopenharmony_ci struct lm80_data *data; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct lm80_data), GFP_KERNEL); 6018c2ecf20Sopenharmony_ci if (!data) 6028c2ecf20Sopenharmony_ci return -ENOMEM; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci data->client = client; 6058c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Initialize the LM80 chip */ 6088c2ecf20Sopenharmony_ci lm80_init_client(client); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* A few vars need to be filled upon startup */ 6118c2ecf20Sopenharmony_ci data->fan[f_min][0] = lm80_read_value(client, LM80_REG_FAN_MIN(1)); 6128c2ecf20Sopenharmony_ci data->fan[f_min][1] = lm80_read_value(client, LM80_REG_FAN_MIN(2)); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 6158c2ecf20Sopenharmony_ci data, lm80_groups); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci/* 6218c2ecf20Sopenharmony_ci * Driver data (common to all clients) 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic const struct i2c_device_id lm80_id[] = { 6258c2ecf20Sopenharmony_ci { "lm80", 0 }, 6268c2ecf20Sopenharmony_ci { "lm96080", 1 }, 6278c2ecf20Sopenharmony_ci { } 6288c2ecf20Sopenharmony_ci}; 6298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lm80_id); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic struct i2c_driver lm80_driver = { 6328c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 6338c2ecf20Sopenharmony_ci .driver = { 6348c2ecf20Sopenharmony_ci .name = "lm80", 6358c2ecf20Sopenharmony_ci }, 6368c2ecf20Sopenharmony_ci .probe_new = lm80_probe, 6378c2ecf20Sopenharmony_ci .id_table = lm80_id, 6388c2ecf20Sopenharmony_ci .detect = lm80_detect, 6398c2ecf20Sopenharmony_ci .address_list = normal_i2c, 6408c2ecf20Sopenharmony_ci}; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cimodule_i2c_driver(lm80_driver); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " 6458c2ecf20Sopenharmony_ci "Philip Edelbrock <phil@netroedge.com>"); 6468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LM80 driver"); 6478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 648