18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * adm1031.c - Part of lm_sensors, Linux kernel modules for hardware 48c2ecf20Sopenharmony_ci * monitoring 58c2ecf20Sopenharmony_ci * Based on lm75.c and lm85.c 68c2ecf20Sopenharmony_ci * Supports adm1030 / adm1031 78c2ecf20Sopenharmony_ci * Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org> 88c2ecf20Sopenharmony_ci * Reworked by Jean Delvare <jdelvare@suse.de> 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/* Following macros takes channel parameter starting from 0 to 2 */ 228c2ecf20Sopenharmony_ci#define ADM1031_REG_FAN_SPEED(nr) (0x08 + (nr)) 238c2ecf20Sopenharmony_ci#define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr)) 248c2ecf20Sopenharmony_ci#define ADM1031_REG_PWM (0x22) 258c2ecf20Sopenharmony_ci#define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr)) 268c2ecf20Sopenharmony_ci#define ADM1031_REG_FAN_FILTER (0x23) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define ADM1031_REG_TEMP_OFFSET(nr) (0x0d + (nr)) 298c2ecf20Sopenharmony_ci#define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4 * (nr)) 308c2ecf20Sopenharmony_ci#define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4 * (nr)) 318c2ecf20Sopenharmony_ci#define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4 * (nr)) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define ADM1031_REG_TEMP(nr) (0x0a + (nr)) 348c2ecf20Sopenharmony_ci#define ADM1031_REG_AUTO_TEMP(nr) (0x24 + (nr)) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define ADM1031_REG_STATUS(nr) (0x2 + (nr)) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define ADM1031_REG_CONF1 0x00 398c2ecf20Sopenharmony_ci#define ADM1031_REG_CONF2 0x01 408c2ecf20Sopenharmony_ci#define ADM1031_REG_EXT_TEMP 0x06 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define ADM1031_CONF1_MONITOR_ENABLE 0x01 /* Monitoring enable */ 438c2ecf20Sopenharmony_ci#define ADM1031_CONF1_PWM_INVERT 0x08 /* PWM Invert */ 448c2ecf20Sopenharmony_ci#define ADM1031_CONF1_AUTO_MODE 0x80 /* Auto FAN */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define ADM1031_CONF2_PWM1_ENABLE 0x01 478c2ecf20Sopenharmony_ci#define ADM1031_CONF2_PWM2_ENABLE 0x02 488c2ecf20Sopenharmony_ci#define ADM1031_CONF2_TACH1_ENABLE 0x04 498c2ecf20Sopenharmony_ci#define ADM1031_CONF2_TACH2_ENABLE 0x08 508c2ecf20Sopenharmony_ci#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan)) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define ADM1031_UPDATE_RATE_MASK 0x1c 538c2ecf20Sopenharmony_ci#define ADM1031_UPDATE_RATE_SHIFT 2 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Addresses to scan */ 568c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cienum chips { adm1030, adm1031 }; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_citypedef u8 auto_chan_table_t[8][2]; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* Each client has this additional data */ 638c2ecf20Sopenharmony_cistruct adm1031_data { 648c2ecf20Sopenharmony_ci struct i2c_client *client; 658c2ecf20Sopenharmony_ci const struct attribute_group *groups[3]; 668c2ecf20Sopenharmony_ci struct mutex update_lock; 678c2ecf20Sopenharmony_ci int chip_type; 688c2ecf20Sopenharmony_ci char valid; /* !=0 if following fields are valid */ 698c2ecf20Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 708c2ecf20Sopenharmony_ci unsigned int update_interval; /* In milliseconds */ 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * The chan_select_table contains the possible configurations for 738c2ecf20Sopenharmony_ci * auto fan control. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci const auto_chan_table_t *chan_select_table; 768c2ecf20Sopenharmony_ci u16 alarm; 778c2ecf20Sopenharmony_ci u8 conf1; 788c2ecf20Sopenharmony_ci u8 conf2; 798c2ecf20Sopenharmony_ci u8 fan[2]; 808c2ecf20Sopenharmony_ci u8 fan_div[2]; 818c2ecf20Sopenharmony_ci u8 fan_min[2]; 828c2ecf20Sopenharmony_ci u8 pwm[2]; 838c2ecf20Sopenharmony_ci u8 old_pwm[2]; 848c2ecf20Sopenharmony_ci s8 temp[3]; 858c2ecf20Sopenharmony_ci u8 ext_temp[3]; 868c2ecf20Sopenharmony_ci u8 auto_temp[3]; 878c2ecf20Sopenharmony_ci u8 auto_temp_min[3]; 888c2ecf20Sopenharmony_ci u8 auto_temp_off[3]; 898c2ecf20Sopenharmony_ci u8 auto_temp_max[3]; 908c2ecf20Sopenharmony_ci s8 temp_offset[3]; 918c2ecf20Sopenharmony_ci s8 temp_min[3]; 928c2ecf20Sopenharmony_ci s8 temp_max[3]; 938c2ecf20Sopenharmony_ci s8 temp_crit[3]; 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline u8 adm1031_read_value(struct i2c_client *client, u8 reg) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return i2c_smbus_read_byte_data(client, reg); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic inline int 1028c2ecf20Sopenharmony_ciadm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(client, reg, value); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic struct adm1031_data *adm1031_update_device(struct device *dev) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 1108c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1118c2ecf20Sopenharmony_ci unsigned long next_update; 1128c2ecf20Sopenharmony_ci int chan; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci next_update = data->last_updated 1178c2ecf20Sopenharmony_ci + msecs_to_jiffies(data->update_interval); 1188c2ecf20Sopenharmony_ci if (time_after(jiffies, next_update) || !data->valid) { 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "Starting adm1031 update\n"); 1218c2ecf20Sopenharmony_ci for (chan = 0; 1228c2ecf20Sopenharmony_ci chan < ((data->chip_type == adm1031) ? 3 : 2); chan++) { 1238c2ecf20Sopenharmony_ci u8 oldh, newh; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci oldh = 1268c2ecf20Sopenharmony_ci adm1031_read_value(client, ADM1031_REG_TEMP(chan)); 1278c2ecf20Sopenharmony_ci data->ext_temp[chan] = 1288c2ecf20Sopenharmony_ci adm1031_read_value(client, ADM1031_REG_EXT_TEMP); 1298c2ecf20Sopenharmony_ci newh = 1308c2ecf20Sopenharmony_ci adm1031_read_value(client, ADM1031_REG_TEMP(chan)); 1318c2ecf20Sopenharmony_ci if (newh != oldh) { 1328c2ecf20Sopenharmony_ci data->ext_temp[chan] = 1338c2ecf20Sopenharmony_ci adm1031_read_value(client, 1348c2ecf20Sopenharmony_ci ADM1031_REG_EXT_TEMP); 1358c2ecf20Sopenharmony_ci#ifdef DEBUG 1368c2ecf20Sopenharmony_ci oldh = 1378c2ecf20Sopenharmony_ci adm1031_read_value(client, 1388c2ecf20Sopenharmony_ci ADM1031_REG_TEMP(chan)); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* oldh is actually newer */ 1418c2ecf20Sopenharmony_ci if (newh != oldh) 1428c2ecf20Sopenharmony_ci dev_warn(&client->dev, 1438c2ecf20Sopenharmony_ci "Remote temperature may be wrong.\n"); 1448c2ecf20Sopenharmony_ci#endif 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci data->temp[chan] = newh; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci data->temp_offset[chan] = 1498c2ecf20Sopenharmony_ci adm1031_read_value(client, 1508c2ecf20Sopenharmony_ci ADM1031_REG_TEMP_OFFSET(chan)); 1518c2ecf20Sopenharmony_ci data->temp_min[chan] = 1528c2ecf20Sopenharmony_ci adm1031_read_value(client, 1538c2ecf20Sopenharmony_ci ADM1031_REG_TEMP_MIN(chan)); 1548c2ecf20Sopenharmony_ci data->temp_max[chan] = 1558c2ecf20Sopenharmony_ci adm1031_read_value(client, 1568c2ecf20Sopenharmony_ci ADM1031_REG_TEMP_MAX(chan)); 1578c2ecf20Sopenharmony_ci data->temp_crit[chan] = 1588c2ecf20Sopenharmony_ci adm1031_read_value(client, 1598c2ecf20Sopenharmony_ci ADM1031_REG_TEMP_CRIT(chan)); 1608c2ecf20Sopenharmony_ci data->auto_temp[chan] = 1618c2ecf20Sopenharmony_ci adm1031_read_value(client, 1628c2ecf20Sopenharmony_ci ADM1031_REG_AUTO_TEMP(chan)); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1); 1678c2ecf20Sopenharmony_ci data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0)) 1708c2ecf20Sopenharmony_ci | (adm1031_read_value(client, ADM1031_REG_STATUS(1)) << 8); 1718c2ecf20Sopenharmony_ci if (data->chip_type == adm1030) 1728c2ecf20Sopenharmony_ci data->alarm &= 0xc0ff; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci for (chan = 0; chan < (data->chip_type == adm1030 ? 1 : 2); 1758c2ecf20Sopenharmony_ci chan++) { 1768c2ecf20Sopenharmony_ci data->fan_div[chan] = 1778c2ecf20Sopenharmony_ci adm1031_read_value(client, 1788c2ecf20Sopenharmony_ci ADM1031_REG_FAN_DIV(chan)); 1798c2ecf20Sopenharmony_ci data->fan_min[chan] = 1808c2ecf20Sopenharmony_ci adm1031_read_value(client, 1818c2ecf20Sopenharmony_ci ADM1031_REG_FAN_MIN(chan)); 1828c2ecf20Sopenharmony_ci data->fan[chan] = 1838c2ecf20Sopenharmony_ci adm1031_read_value(client, 1848c2ecf20Sopenharmony_ci ADM1031_REG_FAN_SPEED(chan)); 1858c2ecf20Sopenharmony_ci data->pwm[chan] = 1868c2ecf20Sopenharmony_ci (adm1031_read_value(client, 1878c2ecf20Sopenharmony_ci ADM1031_REG_PWM) >> (4 * chan)) & 0x0f; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci data->last_updated = jiffies; 1908c2ecf20Sopenharmony_ci data->valid = 1; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return data; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci#define TEMP_TO_REG(val) (((val) < 0 ? ((val - 500) / 1000) : \ 1998c2ecf20Sopenharmony_ci ((val + 500) / 1000))) 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci#define TEMP_FROM_REG(val) ((val) * 1000) 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci#define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125) 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci#define TEMP_OFFSET_TO_REG(val) (TEMP_TO_REG(val) & 0x8f) 2068c2ecf20Sopenharmony_ci#define TEMP_OFFSET_FROM_REG(val) TEMP_FROM_REG((val) < 0 ? \ 2078c2ecf20Sopenharmony_ci (val) | 0x70 : (val)) 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#define FAN_FROM_REG(reg, div) ((reg) ? \ 2108c2ecf20Sopenharmony_ci (11250 * 60) / ((reg) * (div)) : 0) 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int FAN_TO_REG(int reg, int div) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci int tmp; 2158c2ecf20Sopenharmony_ci tmp = FAN_FROM_REG(clamp_val(reg, 0, 65535), div); 2168c2ecf20Sopenharmony_ci return tmp > 255 ? 255 : tmp; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci#define FAN_DIV_FROM_REG(reg) (1<<(((reg)&0xc0)>>6)) 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci#define PWM_TO_REG(val) (clamp_val((val), 0, 255) >> 4) 2228c2ecf20Sopenharmony_ci#define PWM_FROM_REG(val) ((val) << 4) 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci#define FAN_CHAN_FROM_REG(reg) (((reg) >> 5) & 7) 2258c2ecf20Sopenharmony_ci#define FAN_CHAN_TO_REG(val, reg) \ 2268c2ecf20Sopenharmony_ci (((reg) & 0x1F) | (((val) << 5) & 0xe0)) 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci#define AUTO_TEMP_MIN_TO_REG(val, reg) \ 2298c2ecf20Sopenharmony_ci ((((val) / 500) & 0xf8) | ((reg) & 0x7)) 2308c2ecf20Sopenharmony_ci#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1 << ((reg) & 0x7))) 2318c2ecf20Sopenharmony_ci#define AUTO_TEMP_MIN_FROM_REG(reg) (1000 * ((((reg) >> 3) & 0x1f) << 2)) 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci#define AUTO_TEMP_MIN_FROM_REG_DEG(reg) ((((reg) >> 3) & 0x1f) << 2) 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci#define AUTO_TEMP_OFF_FROM_REG(reg) \ 2368c2ecf20Sopenharmony_ci (AUTO_TEMP_MIN_FROM_REG(reg) - 5000) 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci#define AUTO_TEMP_MAX_FROM_REG(reg) \ 2398c2ecf20Sopenharmony_ci (AUTO_TEMP_RANGE_FROM_REG(reg) + \ 2408c2ecf20Sopenharmony_ci AUTO_TEMP_MIN_FROM_REG(reg)) 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci int ret; 2458c2ecf20Sopenharmony_ci int range = val - AUTO_TEMP_MIN_FROM_REG(reg); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci range = ((val - AUTO_TEMP_MIN_FROM_REG(reg))*10)/(16 - pwm); 2488c2ecf20Sopenharmony_ci ret = ((reg & 0xf8) | 2498c2ecf20Sopenharmony_ci (range < 10000 ? 0 : 2508c2ecf20Sopenharmony_ci range < 20000 ? 1 : 2518c2ecf20Sopenharmony_ci range < 40000 ? 2 : range < 80000 ? 3 : 4)); 2528c2ecf20Sopenharmony_ci return ret; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* FAN auto control */ 2568c2ecf20Sopenharmony_ci#define GET_FAN_AUTO_BITFIELD(data, idx) \ 2578c2ecf20Sopenharmony_ci (*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx % 2] 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/* 2608c2ecf20Sopenharmony_ci * The tables below contains the possible values for the auto fan 2618c2ecf20Sopenharmony_ci * control bitfields. the index in the table is the register value. 2628c2ecf20Sopenharmony_ci * MSb is the auto fan control enable bit, so the four first entries 2638c2ecf20Sopenharmony_ci * in the table disables auto fan control when both bitfields are zero. 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_cistatic const auto_chan_table_t auto_channel_select_table_adm1031 = { 2668c2ecf20Sopenharmony_ci { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, 2678c2ecf20Sopenharmony_ci { 2 /* 0b010 */ , 4 /* 0b100 */ }, 2688c2ecf20Sopenharmony_ci { 2 /* 0b010 */ , 2 /* 0b010 */ }, 2698c2ecf20Sopenharmony_ci { 4 /* 0b100 */ , 4 /* 0b100 */ }, 2708c2ecf20Sopenharmony_ci { 7 /* 0b111 */ , 7 /* 0b111 */ }, 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic const auto_chan_table_t auto_channel_select_table_adm1030 = { 2748c2ecf20Sopenharmony_ci { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, 2758c2ecf20Sopenharmony_ci { 2 /* 0b10 */ , 0 }, 2768c2ecf20Sopenharmony_ci { 0xff /* invalid */ , 0 }, 2778c2ecf20Sopenharmony_ci { 0xff /* invalid */ , 0 }, 2788c2ecf20Sopenharmony_ci { 3 /* 0b11 */ , 0 }, 2798c2ecf20Sopenharmony_ci}; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* 2828c2ecf20Sopenharmony_ci * That function checks if a bitfield is valid and returns the other bitfield 2838c2ecf20Sopenharmony_ci * nearest match if no exact match where found. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_cistatic int 2868c2ecf20Sopenharmony_ciget_fan_auto_nearest(struct adm1031_data *data, int chan, u8 val, u8 reg) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int i; 2898c2ecf20Sopenharmony_ci int first_match = -1, exact_match = -1; 2908c2ecf20Sopenharmony_ci u8 other_reg_val = 2918c2ecf20Sopenharmony_ci (*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1]; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (val == 0) 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 2978c2ecf20Sopenharmony_ci if ((val == (*data->chan_select_table)[i][chan]) && 2988c2ecf20Sopenharmony_ci ((*data->chan_select_table)[i][chan ? 0 : 1] == 2998c2ecf20Sopenharmony_ci other_reg_val)) { 3008c2ecf20Sopenharmony_ci /* We found an exact match */ 3018c2ecf20Sopenharmony_ci exact_match = i; 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci } else if (val == (*data->chan_select_table)[i][chan] && 3048c2ecf20Sopenharmony_ci first_match == -1) { 3058c2ecf20Sopenharmony_ci /* 3068c2ecf20Sopenharmony_ci * Save the first match in case of an exact match has 3078c2ecf20Sopenharmony_ci * not been found 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_ci first_match = i; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (exact_match >= 0) 3148c2ecf20Sopenharmony_ci return exact_match; 3158c2ecf20Sopenharmony_ci else if (first_match >= 0) 3168c2ecf20Sopenharmony_ci return first_match; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return -EINVAL; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic ssize_t fan_auto_channel_show(struct device *dev, 3228c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 3258c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 3268c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", GET_FAN_AUTO_BITFIELD(data, nr)); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic ssize_t 3308c2ecf20Sopenharmony_cifan_auto_channel_store(struct device *dev, struct device_attribute *attr, 3318c2ecf20Sopenharmony_ci const char *buf, size_t count) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 3348c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 3358c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 3368c2ecf20Sopenharmony_ci long val; 3378c2ecf20Sopenharmony_ci u8 reg; 3388c2ecf20Sopenharmony_ci int ret; 3398c2ecf20Sopenharmony_ci u8 old_fan_mode; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &val); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci return ret; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci old_fan_mode = data->conf1; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ret = get_fan_auto_nearest(data, nr, val, data->conf1); 3508c2ecf20Sopenharmony_ci if (ret < 0) { 3518c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3528c2ecf20Sopenharmony_ci return ret; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci reg = ret; 3558c2ecf20Sopenharmony_ci data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1); 3568c2ecf20Sopenharmony_ci if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) ^ 3578c2ecf20Sopenharmony_ci (old_fan_mode & ADM1031_CONF1_AUTO_MODE)) { 3588c2ecf20Sopenharmony_ci if (data->conf1 & ADM1031_CONF1_AUTO_MODE) { 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * Switch to Auto Fan Mode 3618c2ecf20Sopenharmony_ci * Save PWM registers 3628c2ecf20Sopenharmony_ci * Set PWM registers to 33% Both 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_ci data->old_pwm[0] = data->pwm[0]; 3658c2ecf20Sopenharmony_ci data->old_pwm[1] = data->pwm[1]; 3668c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_PWM, 0x55); 3678c2ecf20Sopenharmony_ci } else { 3688c2ecf20Sopenharmony_ci /* Switch to Manual Mode */ 3698c2ecf20Sopenharmony_ci data->pwm[0] = data->old_pwm[0]; 3708c2ecf20Sopenharmony_ci data->pwm[1] = data->old_pwm[1]; 3718c2ecf20Sopenharmony_ci /* Restore PWM registers */ 3728c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_PWM, 3738c2ecf20Sopenharmony_ci data->pwm[0] | (data->pwm[1] << 4)); 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1); 3778c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_CONF1, data->conf1); 3788c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3798c2ecf20Sopenharmony_ci return count; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(auto_fan1_channel, fan_auto_channel, 0); 3838c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(auto_fan2_channel, fan_auto_channel, 1); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci/* Auto Temps */ 3868c2ecf20Sopenharmony_cistatic ssize_t auto_temp_off_show(struct device *dev, 3878c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 3908c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 3918c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 3928c2ecf20Sopenharmony_ci AUTO_TEMP_OFF_FROM_REG(data->auto_temp[nr])); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_cistatic ssize_t auto_temp_min_show(struct device *dev, 3958c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 3988c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 3998c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 4008c2ecf20Sopenharmony_ci AUTO_TEMP_MIN_FROM_REG(data->auto_temp[nr])); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_cistatic ssize_t 4038c2ecf20Sopenharmony_ciauto_temp_min_store(struct device *dev, struct device_attribute *attr, 4048c2ecf20Sopenharmony_ci const char *buf, size_t count) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 4078c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 4088c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 4098c2ecf20Sopenharmony_ci long val; 4108c2ecf20Sopenharmony_ci int ret; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &val); 4138c2ecf20Sopenharmony_ci if (ret) 4148c2ecf20Sopenharmony_ci return ret; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci val = clamp_val(val, 0, 127000); 4178c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 4188c2ecf20Sopenharmony_ci data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]); 4198c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr), 4208c2ecf20Sopenharmony_ci data->auto_temp[nr]); 4218c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4228c2ecf20Sopenharmony_ci return count; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_cistatic ssize_t auto_temp_max_show(struct device *dev, 4258c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 4288c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 4298c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 4308c2ecf20Sopenharmony_ci AUTO_TEMP_MAX_FROM_REG(data->auto_temp[nr])); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_cistatic ssize_t 4338c2ecf20Sopenharmony_ciauto_temp_max_store(struct device *dev, struct device_attribute *attr, 4348c2ecf20Sopenharmony_ci const char *buf, size_t count) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 4378c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 4388c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 4398c2ecf20Sopenharmony_ci long val; 4408c2ecf20Sopenharmony_ci int ret; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &val); 4438c2ecf20Sopenharmony_ci if (ret) 4448c2ecf20Sopenharmony_ci return ret; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci val = clamp_val(val, 0, 127000); 4478c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 4488c2ecf20Sopenharmony_ci data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr], 4498c2ecf20Sopenharmony_ci data->pwm[nr]); 4508c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr), 4518c2ecf20Sopenharmony_ci data->temp_max[nr]); 4528c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4538c2ecf20Sopenharmony_ci return count; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(auto_temp1_off, auto_temp_off, 0); 4578c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(auto_temp1_min, auto_temp_min, 0); 4588c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(auto_temp1_max, auto_temp_max, 0); 4598c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(auto_temp2_off, auto_temp_off, 1); 4608c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(auto_temp2_min, auto_temp_min, 1); 4618c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(auto_temp2_max, auto_temp_max, 1); 4628c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(auto_temp3_off, auto_temp_off, 2); 4638c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(auto_temp3_min, auto_temp_min, 2); 4648c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(auto_temp3_max, auto_temp_max, 2); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/* pwm */ 4678c2ecf20Sopenharmony_cistatic ssize_t pwm_show(struct device *dev, struct device_attribute *attr, 4688c2ecf20Sopenharmony_ci char *buf) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 4718c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 4728c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr])); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_cistatic ssize_t pwm_store(struct device *dev, struct device_attribute *attr, 4758c2ecf20Sopenharmony_ci const char *buf, size_t count) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 4788c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 4798c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 4808c2ecf20Sopenharmony_ci long val; 4818c2ecf20Sopenharmony_ci int ret, reg; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &val); 4848c2ecf20Sopenharmony_ci if (ret) 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 4888c2ecf20Sopenharmony_ci if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) && 4898c2ecf20Sopenharmony_ci (((val>>4) & 0xf) != 5)) { 4908c2ecf20Sopenharmony_ci /* In automatic mode, the only PWM accepted is 33% */ 4918c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4928c2ecf20Sopenharmony_ci return -EINVAL; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci data->pwm[nr] = PWM_TO_REG(val); 4958c2ecf20Sopenharmony_ci reg = adm1031_read_value(client, ADM1031_REG_PWM); 4968c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_PWM, 4978c2ecf20Sopenharmony_ci nr ? ((data->pwm[nr] << 4) & 0xf0) | (reg & 0xf) 4988c2ecf20Sopenharmony_ci : (data->pwm[nr] & 0xf) | (reg & 0xf0)); 4998c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 5008c2ecf20Sopenharmony_ci return count; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0); 5048c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1); 5058c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(auto_fan1_min_pwm, pwm, 0); 5068c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(auto_fan2_min_pwm, pwm, 1); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci/* Fans */ 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/* 5118c2ecf20Sopenharmony_ci * That function checks the cases where the fan reading is not 5128c2ecf20Sopenharmony_ci * relevant. It is used to provide 0 as fan reading when the fan is 5138c2ecf20Sopenharmony_ci * not supposed to run 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_cistatic int trust_fan_readings(struct adm1031_data *data, int chan) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci int res = 0; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (data->conf1 & ADM1031_CONF1_AUTO_MODE) { 5208c2ecf20Sopenharmony_ci switch (data->conf1 & 0x60) { 5218c2ecf20Sopenharmony_ci case 0x00: 5228c2ecf20Sopenharmony_ci /* 5238c2ecf20Sopenharmony_ci * remote temp1 controls fan1, 5248c2ecf20Sopenharmony_ci * remote temp2 controls fan2 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ci res = data->temp[chan+1] >= 5278c2ecf20Sopenharmony_ci AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]); 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci case 0x20: /* remote temp1 controls both fans */ 5308c2ecf20Sopenharmony_ci res = 5318c2ecf20Sopenharmony_ci data->temp[1] >= 5328c2ecf20Sopenharmony_ci AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]); 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci case 0x40: /* remote temp2 controls both fans */ 5358c2ecf20Sopenharmony_ci res = 5368c2ecf20Sopenharmony_ci data->temp[2] >= 5378c2ecf20Sopenharmony_ci AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]); 5388c2ecf20Sopenharmony_ci break; 5398c2ecf20Sopenharmony_ci case 0x60: /* max controls both fans */ 5408c2ecf20Sopenharmony_ci res = 5418c2ecf20Sopenharmony_ci data->temp[0] >= 5428c2ecf20Sopenharmony_ci AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[0]) 5438c2ecf20Sopenharmony_ci || data->temp[1] >= 5448c2ecf20Sopenharmony_ci AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]) 5458c2ecf20Sopenharmony_ci || (data->chip_type == adm1031 5468c2ecf20Sopenharmony_ci && data->temp[2] >= 5478c2ecf20Sopenharmony_ci AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2])); 5488c2ecf20Sopenharmony_ci break; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci } else { 5518c2ecf20Sopenharmony_ci res = data->pwm[chan] > 0; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci return res; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic ssize_t fan_show(struct device *dev, struct device_attribute *attr, 5578c2ecf20Sopenharmony_ci char *buf) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 5608c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 5618c2ecf20Sopenharmony_ci int value; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci value = trust_fan_readings(data, nr) ? FAN_FROM_REG(data->fan[nr], 5648c2ecf20Sopenharmony_ci FAN_DIV_FROM_REG(data->fan_div[nr])) : 0; 5658c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", value); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic ssize_t fan_div_show(struct device *dev, struct device_attribute *attr, 5698c2ecf20Sopenharmony_ci char *buf) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 5728c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 5738c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[nr])); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_cistatic ssize_t fan_min_show(struct device *dev, struct device_attribute *attr, 5768c2ecf20Sopenharmony_ci char *buf) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 5798c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 5808c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 5818c2ecf20Sopenharmony_ci FAN_FROM_REG(data->fan_min[nr], 5828c2ecf20Sopenharmony_ci FAN_DIV_FROM_REG(data->fan_div[nr]))); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_cistatic ssize_t fan_min_store(struct device *dev, 5858c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 5868c2ecf20Sopenharmony_ci size_t count) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 5898c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 5908c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 5918c2ecf20Sopenharmony_ci long val; 5928c2ecf20Sopenharmony_ci int ret; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &val); 5958c2ecf20Sopenharmony_ci if (ret) 5968c2ecf20Sopenharmony_ci return ret; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 5998c2ecf20Sopenharmony_ci if (val) { 6008c2ecf20Sopenharmony_ci data->fan_min[nr] = 6018c2ecf20Sopenharmony_ci FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr])); 6028c2ecf20Sopenharmony_ci } else { 6038c2ecf20Sopenharmony_ci data->fan_min[nr] = 0xff; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), data->fan_min[nr]); 6068c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 6078c2ecf20Sopenharmony_ci return count; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_cistatic ssize_t fan_div_store(struct device *dev, 6108c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 6118c2ecf20Sopenharmony_ci size_t count) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 6148c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 6158c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 6168c2ecf20Sopenharmony_ci long val; 6178c2ecf20Sopenharmony_ci u8 tmp; 6188c2ecf20Sopenharmony_ci int old_div; 6198c2ecf20Sopenharmony_ci int new_min; 6208c2ecf20Sopenharmony_ci int ret; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &val); 6238c2ecf20Sopenharmony_ci if (ret) 6248c2ecf20Sopenharmony_ci return ret; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci tmp = val == 8 ? 0xc0 : 6278c2ecf20Sopenharmony_ci val == 4 ? 0x80 : 6288c2ecf20Sopenharmony_ci val == 2 ? 0x40 : 6298c2ecf20Sopenharmony_ci val == 1 ? 0x00 : 6308c2ecf20Sopenharmony_ci 0xff; 6318c2ecf20Sopenharmony_ci if (tmp == 0xff) 6328c2ecf20Sopenharmony_ci return -EINVAL; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 6358c2ecf20Sopenharmony_ci /* Get fresh readings */ 6368c2ecf20Sopenharmony_ci data->fan_div[nr] = adm1031_read_value(client, 6378c2ecf20Sopenharmony_ci ADM1031_REG_FAN_DIV(nr)); 6388c2ecf20Sopenharmony_ci data->fan_min[nr] = adm1031_read_value(client, 6398c2ecf20Sopenharmony_ci ADM1031_REG_FAN_MIN(nr)); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* Write the new clock divider and fan min */ 6428c2ecf20Sopenharmony_ci old_div = FAN_DIV_FROM_REG(data->fan_div[nr]); 6438c2ecf20Sopenharmony_ci data->fan_div[nr] = tmp | (0x3f & data->fan_div[nr]); 6448c2ecf20Sopenharmony_ci new_min = data->fan_min[nr] * old_div / val; 6458c2ecf20Sopenharmony_ci data->fan_min[nr] = new_min > 0xff ? 0xff : new_min; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_FAN_DIV(nr), 6488c2ecf20Sopenharmony_ci data->fan_div[nr]); 6498c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), 6508c2ecf20Sopenharmony_ci data->fan_min[nr]); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* Invalidate the cache: fan speed is no longer valid */ 6538c2ecf20Sopenharmony_ci data->valid = 0; 6548c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 6558c2ecf20Sopenharmony_ci return count; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0); 6598c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); 6608c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0); 6618c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1); 6628c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); 6638c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci/* Temps */ 6668c2ecf20Sopenharmony_cistatic ssize_t temp_show(struct device *dev, struct device_attribute *attr, 6678c2ecf20Sopenharmony_ci char *buf) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 6708c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 6718c2ecf20Sopenharmony_ci int ext; 6728c2ecf20Sopenharmony_ci ext = nr == 0 ? 6738c2ecf20Sopenharmony_ci ((data->ext_temp[nr] >> 6) & 0x3) * 2 : 6748c2ecf20Sopenharmony_ci (((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7)); 6758c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext)); 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_cistatic ssize_t temp_offset_show(struct device *dev, 6788c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 6818c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 6828c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 6838c2ecf20Sopenharmony_ci TEMP_OFFSET_FROM_REG(data->temp_offset[nr])); 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_cistatic ssize_t temp_min_show(struct device *dev, 6868c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 6898c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 6908c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr])); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_cistatic ssize_t temp_max_show(struct device *dev, 6938c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 6968c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 6978c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr])); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_cistatic ssize_t temp_crit_show(struct device *dev, 7008c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 7038c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 7048c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr])); 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_cistatic ssize_t temp_offset_store(struct device *dev, 7078c2ecf20Sopenharmony_ci struct device_attribute *attr, 7088c2ecf20Sopenharmony_ci const char *buf, size_t count) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 7118c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 7128c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 7138c2ecf20Sopenharmony_ci long val; 7148c2ecf20Sopenharmony_ci int ret; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &val); 7178c2ecf20Sopenharmony_ci if (ret) 7188c2ecf20Sopenharmony_ci return ret; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci val = clamp_val(val, -15000, 15000); 7218c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 7228c2ecf20Sopenharmony_ci data->temp_offset[nr] = TEMP_OFFSET_TO_REG(val); 7238c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_TEMP_OFFSET(nr), 7248c2ecf20Sopenharmony_ci data->temp_offset[nr]); 7258c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7268c2ecf20Sopenharmony_ci return count; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_cistatic ssize_t temp_min_store(struct device *dev, 7298c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 7308c2ecf20Sopenharmony_ci size_t count) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 7338c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 7348c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 7358c2ecf20Sopenharmony_ci long val; 7368c2ecf20Sopenharmony_ci int ret; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &val); 7398c2ecf20Sopenharmony_ci if (ret) 7408c2ecf20Sopenharmony_ci return ret; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci val = clamp_val(val, -55000, 127000); 7438c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 7448c2ecf20Sopenharmony_ci data->temp_min[nr] = TEMP_TO_REG(val); 7458c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_TEMP_MIN(nr), 7468c2ecf20Sopenharmony_ci data->temp_min[nr]); 7478c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7488c2ecf20Sopenharmony_ci return count; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_cistatic ssize_t temp_max_store(struct device *dev, 7518c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 7528c2ecf20Sopenharmony_ci size_t count) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 7558c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 7568c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 7578c2ecf20Sopenharmony_ci long val; 7588c2ecf20Sopenharmony_ci int ret; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &val); 7618c2ecf20Sopenharmony_ci if (ret) 7628c2ecf20Sopenharmony_ci return ret; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci val = clamp_val(val, -55000, 127000); 7658c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 7668c2ecf20Sopenharmony_ci data->temp_max[nr] = TEMP_TO_REG(val); 7678c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_TEMP_MAX(nr), 7688c2ecf20Sopenharmony_ci data->temp_max[nr]); 7698c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7708c2ecf20Sopenharmony_ci return count; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_cistatic ssize_t temp_crit_store(struct device *dev, 7738c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 7748c2ecf20Sopenharmony_ci size_t count) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 7778c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 7788c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 7798c2ecf20Sopenharmony_ci long val; 7808c2ecf20Sopenharmony_ci int ret; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &val); 7838c2ecf20Sopenharmony_ci if (ret) 7848c2ecf20Sopenharmony_ci return ret; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci val = clamp_val(val, -55000, 127000); 7878c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 7888c2ecf20Sopenharmony_ci data->temp_crit[nr] = TEMP_TO_REG(val); 7898c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_TEMP_CRIT(nr), 7908c2ecf20Sopenharmony_ci data->temp_crit[nr]); 7918c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 7928c2ecf20Sopenharmony_ci return count; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); 7968c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_offset, temp_offset, 0); 7978c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0); 7988c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0); 7998c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_crit, temp_crit, 0); 8008c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); 8018c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_offset, temp_offset, 1); 8028c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1); 8038c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1); 8048c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_crit, temp_crit, 1); 8058c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); 8068c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp3_offset, temp_offset, 2); 8078c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2); 8088c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2); 8098c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp3_crit, temp_crit, 2); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/* Alarms */ 8128c2ecf20Sopenharmony_cistatic ssize_t alarms_show(struct device *dev, struct device_attribute *attr, 8138c2ecf20Sopenharmony_ci char *buf) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 8168c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->alarm); 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(alarms); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic ssize_t alarm_show(struct device *dev, struct device_attribute *attr, 8228c2ecf20Sopenharmony_ci char *buf) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci int bitnr = to_sensor_dev_attr(attr)->index; 8258c2ecf20Sopenharmony_ci struct adm1031_data *data = adm1031_update_device(dev); 8268c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (data->alarm >> bitnr) & 1); 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 0); 8308c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_fault, alarm, 1); 8318c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 2); 8328c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3); 8338c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 4); 8348c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 5); 8358c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 6); 8368c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 7); 8378c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 8); 8388c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan2_fault, alarm, 9); 8398c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, alarm, 10); 8408c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_min_alarm, alarm, 11); 8418c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, 12); 8428c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 13); 8438c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 14); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/* Update Interval */ 8468c2ecf20Sopenharmony_cistatic const unsigned int update_intervals[] = { 8478c2ecf20Sopenharmony_ci 16000, 8000, 4000, 2000, 1000, 500, 250, 125, 8488c2ecf20Sopenharmony_ci}; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic ssize_t update_interval_show(struct device *dev, 8518c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", data->update_interval); 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic ssize_t update_interval_store(struct device *dev, 8598c2ecf20Sopenharmony_ci struct device_attribute *attr, 8608c2ecf20Sopenharmony_ci const char *buf, size_t count) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct adm1031_data *data = dev_get_drvdata(dev); 8638c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 8648c2ecf20Sopenharmony_ci unsigned long val; 8658c2ecf20Sopenharmony_ci int i, err; 8668c2ecf20Sopenharmony_ci u8 reg; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci err = kstrtoul(buf, 10, &val); 8698c2ecf20Sopenharmony_ci if (err) 8708c2ecf20Sopenharmony_ci return err; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* 8738c2ecf20Sopenharmony_ci * Find the nearest update interval from the table. 8748c2ecf20Sopenharmony_ci * Use it to determine the matching update rate. 8758c2ecf20Sopenharmony_ci */ 8768c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(update_intervals) - 1; i++) { 8778c2ecf20Sopenharmony_ci if (val >= update_intervals[i]) 8788c2ecf20Sopenharmony_ci break; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci /* if not found, we point to the last entry (lowest update interval) */ 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* set the new update rate while preserving other settings */ 8838c2ecf20Sopenharmony_ci reg = adm1031_read_value(client, ADM1031_REG_FAN_FILTER); 8848c2ecf20Sopenharmony_ci reg &= ~ADM1031_UPDATE_RATE_MASK; 8858c2ecf20Sopenharmony_ci reg |= i << ADM1031_UPDATE_RATE_SHIFT; 8868c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_FAN_FILTER, reg); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 8898c2ecf20Sopenharmony_ci data->update_interval = update_intervals[i]; 8908c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci return count; 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(update_interval); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic struct attribute *adm1031_attributes[] = { 8988c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_input.dev_attr.attr, 8998c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_div.dev_attr.attr, 9008c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_min.dev_attr.attr, 9018c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_alarm.dev_attr.attr, 9028c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_fault.dev_attr.attr, 9038c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1.dev_attr.attr, 9048c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_fan1_channel.dev_attr.attr, 9058c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 9068c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_offset.dev_attr.attr, 9078c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_min.dev_attr.attr, 9088c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, 9098c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max.dev_attr.attr, 9108c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, 9118c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit.dev_attr.attr, 9128c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, 9138c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_input.dev_attr.attr, 9148c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_offset.dev_attr.attr, 9158c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_min.dev_attr.attr, 9168c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, 9178c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_max.dev_attr.attr, 9188c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, 9198c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_crit.dev_attr.attr, 9208c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, 9218c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_fault.dev_attr.attr, 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_temp1_off.dev_attr.attr, 9248c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_temp1_min.dev_attr.attr, 9258c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_temp1_max.dev_attr.attr, 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_temp2_off.dev_attr.attr, 9288c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_temp2_min.dev_attr.attr, 9298c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_temp2_max.dev_attr.attr, 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_fan1_min_pwm.dev_attr.attr, 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci &dev_attr_update_interval.attr, 9348c2ecf20Sopenharmony_ci &dev_attr_alarms.attr, 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci NULL 9378c2ecf20Sopenharmony_ci}; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic const struct attribute_group adm1031_group = { 9408c2ecf20Sopenharmony_ci .attrs = adm1031_attributes, 9418c2ecf20Sopenharmony_ci}; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic struct attribute *adm1031_attributes_opt[] = { 9448c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_input.dev_attr.attr, 9458c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_div.dev_attr.attr, 9468c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_min.dev_attr.attr, 9478c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_alarm.dev_attr.attr, 9488c2ecf20Sopenharmony_ci &sensor_dev_attr_fan2_fault.dev_attr.attr, 9498c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm2.dev_attr.attr, 9508c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_fan2_channel.dev_attr.attr, 9518c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_input.dev_attr.attr, 9528c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_offset.dev_attr.attr, 9538c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_min.dev_attr.attr, 9548c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, 9558c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_max.dev_attr.attr, 9568c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, 9578c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_crit.dev_attr.attr, 9588c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, 9598c2ecf20Sopenharmony_ci &sensor_dev_attr_temp3_fault.dev_attr.attr, 9608c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_temp3_off.dev_attr.attr, 9618c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_temp3_min.dev_attr.attr, 9628c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_temp3_max.dev_attr.attr, 9638c2ecf20Sopenharmony_ci &sensor_dev_attr_auto_fan2_min_pwm.dev_attr.attr, 9648c2ecf20Sopenharmony_ci NULL 9658c2ecf20Sopenharmony_ci}; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic const struct attribute_group adm1031_group_opt = { 9688c2ecf20Sopenharmony_ci .attrs = adm1031_attributes_opt, 9698c2ecf20Sopenharmony_ci}; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */ 9728c2ecf20Sopenharmony_cistatic int adm1031_detect(struct i2c_client *client, 9738c2ecf20Sopenharmony_ci struct i2c_board_info *info) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 9768c2ecf20Sopenharmony_ci const char *name; 9778c2ecf20Sopenharmony_ci int id, co; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 9808c2ecf20Sopenharmony_ci return -ENODEV; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci id = i2c_smbus_read_byte_data(client, 0x3d); 9838c2ecf20Sopenharmony_ci co = i2c_smbus_read_byte_data(client, 0x3e); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (!((id == 0x31 || id == 0x30) && co == 0x41)) 9868c2ecf20Sopenharmony_ci return -ENODEV; 9878c2ecf20Sopenharmony_ci name = (id == 0x30) ? "adm1030" : "adm1031"; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci strlcpy(info->type, name, I2C_NAME_SIZE); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci return 0; 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic void adm1031_init_client(struct i2c_client *client) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci unsigned int read_val; 9978c2ecf20Sopenharmony_ci unsigned int mask; 9988c2ecf20Sopenharmony_ci int i; 9998c2ecf20Sopenharmony_ci struct adm1031_data *data = i2c_get_clientdata(client); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE); 10028c2ecf20Sopenharmony_ci if (data->chip_type == adm1031) { 10038c2ecf20Sopenharmony_ci mask |= (ADM1031_CONF2_PWM2_ENABLE | 10048c2ecf20Sopenharmony_ci ADM1031_CONF2_TACH2_ENABLE); 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci /* Initialize the ADM1031 chip (enables fan speed reading ) */ 10078c2ecf20Sopenharmony_ci read_val = adm1031_read_value(client, ADM1031_REG_CONF2); 10088c2ecf20Sopenharmony_ci if ((read_val | mask) != read_val) 10098c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci read_val = adm1031_read_value(client, ADM1031_REG_CONF1); 10128c2ecf20Sopenharmony_ci if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) { 10138c2ecf20Sopenharmony_ci adm1031_write_value(client, ADM1031_REG_CONF1, 10148c2ecf20Sopenharmony_ci read_val | ADM1031_CONF1_MONITOR_ENABLE); 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* Read the chip's update rate */ 10188c2ecf20Sopenharmony_ci mask = ADM1031_UPDATE_RATE_MASK; 10198c2ecf20Sopenharmony_ci read_val = adm1031_read_value(client, ADM1031_REG_FAN_FILTER); 10208c2ecf20Sopenharmony_ci i = (read_val & mask) >> ADM1031_UPDATE_RATE_SHIFT; 10218c2ecf20Sopenharmony_ci /* Save it as update interval */ 10228c2ecf20Sopenharmony_ci data->update_interval = update_intervals[i]; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic const struct i2c_device_id adm1031_id[]; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic int adm1031_probe(struct i2c_client *client) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 10308c2ecf20Sopenharmony_ci struct device *hwmon_dev; 10318c2ecf20Sopenharmony_ci struct adm1031_data *data; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct adm1031_data), GFP_KERNEL); 10348c2ecf20Sopenharmony_ci if (!data) 10358c2ecf20Sopenharmony_ci return -ENOMEM; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci i2c_set_clientdata(client, data); 10388c2ecf20Sopenharmony_ci data->client = client; 10398c2ecf20Sopenharmony_ci data->chip_type = i2c_match_id(adm1031_id, client)->driver_data; 10408c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (data->chip_type == adm1030) 10438c2ecf20Sopenharmony_ci data->chan_select_table = &auto_channel_select_table_adm1030; 10448c2ecf20Sopenharmony_ci else 10458c2ecf20Sopenharmony_ci data->chan_select_table = &auto_channel_select_table_adm1031; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci /* Initialize the ADM1031 chip */ 10488c2ecf20Sopenharmony_ci adm1031_init_client(client); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci /* sysfs hooks */ 10518c2ecf20Sopenharmony_ci data->groups[0] = &adm1031_group; 10528c2ecf20Sopenharmony_ci if (data->chip_type == adm1031) 10538c2ecf20Sopenharmony_ci data->groups[1] = &adm1031_group_opt; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 10568c2ecf20Sopenharmony_ci data, data->groups); 10578c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cistatic const struct i2c_device_id adm1031_id[] = { 10618c2ecf20Sopenharmony_ci { "adm1030", adm1030 }, 10628c2ecf20Sopenharmony_ci { "adm1031", adm1031 }, 10638c2ecf20Sopenharmony_ci { } 10648c2ecf20Sopenharmony_ci}; 10658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, adm1031_id); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_cistatic struct i2c_driver adm1031_driver = { 10688c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 10698c2ecf20Sopenharmony_ci .driver = { 10708c2ecf20Sopenharmony_ci .name = "adm1031", 10718c2ecf20Sopenharmony_ci }, 10728c2ecf20Sopenharmony_ci .probe_new = adm1031_probe, 10738c2ecf20Sopenharmony_ci .id_table = adm1031_id, 10748c2ecf20Sopenharmony_ci .detect = adm1031_detect, 10758c2ecf20Sopenharmony_ci .address_list = normal_i2c, 10768c2ecf20Sopenharmony_ci}; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_cimodule_i2c_driver(adm1031_driver); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexandre d'Alton <alex@alexdalton.org>"); 10818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ADM1031/ADM1030 driver"); 10828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1083