11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 26f9703d0SDarrick J. Wong /* 36f9703d0SDarrick J. Wong * A hwmon driver for the Analog Devices ADT7470 46f9703d0SDarrick J. Wong * Copyright (C) 2007 IBM 56f9703d0SDarrick J. Wong * 65407e051SDarrick J. Wong * Author: Darrick J. Wong <darrick.wong@oracle.com> 76f9703d0SDarrick J. Wong */ 86f9703d0SDarrick J. Wong 92e991201SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 102e991201SJoe Perches 116f9703d0SDarrick J. Wong #include <linux/module.h> 126f9703d0SDarrick J. Wong #include <linux/jiffies.h> 136f9703d0SDarrick J. Wong #include <linux/i2c.h> 146f9703d0SDarrick J. Wong #include <linux/hwmon.h> 156f9703d0SDarrick J. Wong #include <linux/hwmon-sysfs.h> 166f9703d0SDarrick J. Wong #include <linux/err.h> 176f9703d0SDarrick J. Wong #include <linux/mutex.h> 186f9703d0SDarrick J. Wong #include <linux/delay.h> 196f9703d0SDarrick J. Wong #include <linux/log2.h> 2089fac11cSDarrick J. Wong #include <linux/kthread.h> 21ef67959cSChris Packham #include <linux/regmap.h> 225a0e3ad6STejun Heo #include <linux/slab.h> 23aa18cc91SJoshua Scott #include <linux/util_macros.h> 246f9703d0SDarrick J. Wong 256f9703d0SDarrick J. Wong /* Addresses to scan */ 2625e9c86dSMark M. Hoffman static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; 276f9703d0SDarrick J. Wong 286f9703d0SDarrick J. Wong /* ADT7470 registers */ 296f9703d0SDarrick J. Wong #define ADT7470_REG_BASE_ADDR 0x20 306f9703d0SDarrick J. Wong #define ADT7470_REG_TEMP_BASE_ADDR 0x20 316f9703d0SDarrick J. Wong #define ADT7470_REG_TEMP_MAX_ADDR 0x29 326f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_BASE_ADDR 0x2A 336f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MAX_ADDR 0x31 346f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_BASE_ADDR 0x32 356f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MAX_ADDR 0x35 366f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MAX_BASE_ADDR 0x38 376f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MAX_MAX_ADDR 0x3B 386f9703d0SDarrick J. Wong #define ADT7470_REG_CFG 0x40 39ef67959cSChris Packham #define ADT7470_STRT_MASK 0x01 40ef67959cSChris Packham #define ADT7470_TEST_MASK 0x02 416f9703d0SDarrick J. Wong #define ADT7470_FSPD_MASK 0x04 42ef67959cSChris Packham #define ADT7470_T05_STB_MASK 0x80 436f9703d0SDarrick J. Wong #define ADT7470_REG_ALARM1 0x41 44fe03f28cSDarrick J. Wong #define ADT7470_R1T_ALARM 0x01 45fe03f28cSDarrick J. Wong #define ADT7470_R2T_ALARM 0x02 46fe03f28cSDarrick J. Wong #define ADT7470_R3T_ALARM 0x04 47fe03f28cSDarrick J. Wong #define ADT7470_R4T_ALARM 0x08 48fe03f28cSDarrick J. Wong #define ADT7470_R5T_ALARM 0x10 49fe03f28cSDarrick J. Wong #define ADT7470_R6T_ALARM 0x20 50fe03f28cSDarrick J. Wong #define ADT7470_R7T_ALARM 0x40 51fe03f28cSDarrick J. Wong #define ADT7470_OOL_ALARM 0x80 526f9703d0SDarrick J. Wong #define ADT7470_REG_ALARM2 0x42 53fe03f28cSDarrick J. Wong #define ADT7470_R8T_ALARM 0x01 54fe03f28cSDarrick J. Wong #define ADT7470_R9T_ALARM 0x02 55fe03f28cSDarrick J. Wong #define ADT7470_R10T_ALARM 0x04 56fe03f28cSDarrick J. Wong #define ADT7470_FAN1_ALARM 0x10 57fe03f28cSDarrick J. Wong #define ADT7470_FAN2_ALARM 0x20 58fe03f28cSDarrick J. Wong #define ADT7470_FAN3_ALARM 0x40 59fe03f28cSDarrick J. Wong #define ADT7470_FAN4_ALARM 0x80 606f9703d0SDarrick J. Wong #define ADT7470_REG_TEMP_LIMITS_BASE_ADDR 0x44 616f9703d0SDarrick J. Wong #define ADT7470_REG_TEMP_LIMITS_MAX_ADDR 0x57 626f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MIN_BASE_ADDR 0x58 636f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MIN_MAX_ADDR 0x5F 646f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MAX_BASE_ADDR 0x60 656f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MAX_MAX_ADDR 0x67 666f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_CFG_BASE_ADDR 0x68 676f9703d0SDarrick J. Wong #define ADT7470_REG_PWM12_CFG 0x68 686f9703d0SDarrick J. Wong #define ADT7470_PWM2_AUTO_MASK 0x40 696f9703d0SDarrick J. Wong #define ADT7470_PWM1_AUTO_MASK 0x80 702e75a4b7SDarrick J. Wong #define ADT7470_PWM_AUTO_MASK 0xC0 716f9703d0SDarrick J. Wong #define ADT7470_REG_PWM34_CFG 0x69 726f9703d0SDarrick J. Wong #define ADT7470_PWM3_AUTO_MASK 0x40 736f9703d0SDarrick J. Wong #define ADT7470_PWM4_AUTO_MASK 0x80 746f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A 756f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D 766f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E 776f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71 78aa18cc91SJoshua Scott #define ADT7470_REG_CFG_2 0x74 796f9703d0SDarrick J. Wong #define ADT7470_REG_ACOUSTICS12 0x75 806f9703d0SDarrick J. Wong #define ADT7470_REG_ACOUSTICS34 0x76 816f9703d0SDarrick J. Wong #define ADT7470_REG_DEVICE 0x3D 826f9703d0SDarrick J. Wong #define ADT7470_REG_VENDOR 0x3E 836f9703d0SDarrick J. Wong #define ADT7470_REG_REVISION 0x3F 846f9703d0SDarrick J. Wong #define ADT7470_REG_ALARM1_MASK 0x72 856f9703d0SDarrick J. Wong #define ADT7470_REG_ALARM2_MASK 0x73 866f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_AUTO_TEMP_BASE_ADDR 0x7C 876f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_AUTO_TEMP_MAX_ADDR 0x7D 886f9703d0SDarrick J. Wong #define ADT7470_REG_MAX_ADDR 0x81 896f9703d0SDarrick J. Wong 906f9703d0SDarrick J. Wong #define ADT7470_TEMP_COUNT 10 916f9703d0SDarrick J. Wong #define ADT7470_TEMP_REG(x) (ADT7470_REG_TEMP_BASE_ADDR + (x)) 926f9703d0SDarrick J. Wong #define ADT7470_TEMP_MIN_REG(x) (ADT7470_REG_TEMP_LIMITS_BASE_ADDR + ((x) * 2)) 936f9703d0SDarrick J. Wong #define ADT7470_TEMP_MAX_REG(x) (ADT7470_REG_TEMP_LIMITS_BASE_ADDR + \ 946f9703d0SDarrick J. Wong ((x) * 2) + 1) 956f9703d0SDarrick J. Wong 966f9703d0SDarrick J. Wong #define ADT7470_FAN_COUNT 4 976f9703d0SDarrick J. Wong #define ADT7470_REG_FAN(x) (ADT7470_REG_FAN_BASE_ADDR + ((x) * 2)) 986f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MIN(x) (ADT7470_REG_FAN_MIN_BASE_ADDR + ((x) * 2)) 996f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MAX(x) (ADT7470_REG_FAN_MAX_BASE_ADDR + ((x) * 2)) 1006f9703d0SDarrick J. Wong 1016f9703d0SDarrick J. Wong #define ADT7470_PWM_COUNT 4 1026f9703d0SDarrick J. Wong #define ADT7470_REG_PWM(x) (ADT7470_REG_PWM_BASE_ADDR + (x)) 1036f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MAX(x) (ADT7470_REG_PWM_MAX_BASE_ADDR + (x)) 1046f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MIN(x) (ADT7470_REG_PWM_MIN_BASE_ADDR + (x)) 1056f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_TMIN(x) (ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR + (x)) 1066f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_CFG(x) (ADT7470_REG_PWM_CFG_BASE_ADDR + ((x) / 2)) 1076f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_AUTO_TEMP(x) (ADT7470_REG_PWM_AUTO_TEMP_BASE_ADDR + \ 1086f9703d0SDarrick J. Wong ((x) / 2)) 1096f9703d0SDarrick J. Wong 110fe03f28cSDarrick J. Wong #define ALARM2(x) ((x) << 8) 111fe03f28cSDarrick J. Wong 1126f9703d0SDarrick J. Wong #define ADT7470_VENDOR 0x41 1136f9703d0SDarrick J. Wong #define ADT7470_DEVICE 0x70 1146f9703d0SDarrick J. Wong /* datasheet only mentions a revision 2 */ 1156f9703d0SDarrick J. Wong #define ADT7470_REVISION 0x02 1166f9703d0SDarrick J. Wong 1176f9703d0SDarrick J. Wong /* "all temps" according to hwmon sysfs interface spec */ 1186f9703d0SDarrick J. Wong #define ADT7470_PWM_ALL_TEMPS 0x3FF 1196f9703d0SDarrick J. Wong 1206f9703d0SDarrick J. Wong /* How often do we reread sensors values? (In jiffies) */ 1216f9703d0SDarrick J. Wong #define SENSOR_REFRESH_INTERVAL (5 * HZ) 1226f9703d0SDarrick J. Wong 1236f9703d0SDarrick J. Wong /* How often do we reread sensor limit values? (In jiffies) */ 1246f9703d0SDarrick J. Wong #define LIMIT_REFRESH_INTERVAL (60 * HZ) 1256f9703d0SDarrick J. Wong 1262f22d5dfSDarrick J. Wong /* Wait at least 200ms per sensor for 10 sensors */ 1272f22d5dfSDarrick J. Wong #define TEMP_COLLECTION_TIME 2000 1286f9703d0SDarrick J. Wong 12989fac11cSDarrick J. Wong /* auto update thing won't fire more than every 2s */ 13089fac11cSDarrick J. Wong #define AUTO_UPDATE_INTERVAL 2000 13189fac11cSDarrick J. Wong 1326f9703d0SDarrick J. Wong /* datasheet says to divide this number by the fan reading to get fan rpm */ 1336f9703d0SDarrick J. Wong #define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) 1346f9703d0SDarrick J. Wong #define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM 1356f9703d0SDarrick J. Wong #define FAN_PERIOD_INVALID 65535 1366f9703d0SDarrick J. Wong #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) 1376f9703d0SDarrick J. Wong 138aa18cc91SJoshua Scott /* Config registers 1 and 2 include fields for selecting the PWM frequency */ 139aa18cc91SJoshua Scott #define ADT7470_CFG_LF 0x40 140aa18cc91SJoshua Scott #define ADT7470_FREQ_MASK 0x70 141aa18cc91SJoshua Scott #define ADT7470_FREQ_SHIFT 4 142aa18cc91SJoshua Scott 1436f9703d0SDarrick J. Wong struct adt7470_data { 144ef67959cSChris Packham struct regmap *regmap; 1456f9703d0SDarrick J. Wong struct mutex lock; 1466f9703d0SDarrick J. Wong char sensors_valid; 1476f9703d0SDarrick J. Wong char limits_valid; 1486f9703d0SDarrick J. Wong unsigned long sensors_last_updated; /* In jiffies */ 1496f9703d0SDarrick J. Wong unsigned long limits_last_updated; /* In jiffies */ 1506f9703d0SDarrick J. Wong 1512f22d5dfSDarrick J. Wong int num_temp_sensors; /* -1 = probe */ 15289fac11cSDarrick J. Wong int temperatures_probed; 1532f22d5dfSDarrick J. Wong 1546f9703d0SDarrick J. Wong s8 temp[ADT7470_TEMP_COUNT]; 1556f9703d0SDarrick J. Wong s8 temp_min[ADT7470_TEMP_COUNT]; 1566f9703d0SDarrick J. Wong s8 temp_max[ADT7470_TEMP_COUNT]; 1576f9703d0SDarrick J. Wong u16 fan[ADT7470_FAN_COUNT]; 1586f9703d0SDarrick J. Wong u16 fan_min[ADT7470_FAN_COUNT]; 1596f9703d0SDarrick J. Wong u16 fan_max[ADT7470_FAN_COUNT]; 160fe03f28cSDarrick J. Wong u16 alarm; 161fe03f28cSDarrick J. Wong u16 alarms_mask; 1626f9703d0SDarrick J. Wong u8 force_pwm_max; 1636f9703d0SDarrick J. Wong u8 pwm[ADT7470_PWM_COUNT]; 1646f9703d0SDarrick J. Wong u8 pwm_max[ADT7470_PWM_COUNT]; 1656f9703d0SDarrick J. Wong u8 pwm_automatic[ADT7470_PWM_COUNT]; 1666f9703d0SDarrick J. Wong u8 pwm_min[ADT7470_PWM_COUNT]; 1676f9703d0SDarrick J. Wong s8 pwm_tmin[ADT7470_PWM_COUNT]; 1686f9703d0SDarrick J. Wong u8 pwm_auto_temp[ADT7470_PWM_COUNT]; 16989fac11cSDarrick J. Wong 17089fac11cSDarrick J. Wong struct task_struct *auto_update; 17189fac11cSDarrick J. Wong unsigned int auto_update_interval; 1726f9703d0SDarrick J. Wong }; 1736f9703d0SDarrick J. Wong 1746f9703d0SDarrick J. Wong /* 1756f9703d0SDarrick J. Wong * 16-bit registers on the ADT7470 are low-byte first. The data sheet says 1766f9703d0SDarrick J. Wong * that the low byte must be read before the high byte. 1776f9703d0SDarrick J. Wong */ 178ef67959cSChris Packham static inline int adt7470_read_word_data(struct adt7470_data *data, unsigned int reg, 179ef67959cSChris Packham unsigned int *val) 1806f9703d0SDarrick J. Wong { 181ef67959cSChris Packham u8 regval[2]; 182ef67959cSChris Packham int err; 18323bd022aSChris Packham 184ef67959cSChris Packham err = regmap_bulk_read(data->regmap, reg, ®val, 2); 185ef67959cSChris Packham if (err < 0) 186ef67959cSChris Packham return err; 187ef67959cSChris Packham 188ef67959cSChris Packham *val = regval[0] | (regval[1] << 8); 189ef67959cSChris Packham 190ef67959cSChris Packham return 0; 1916f9703d0SDarrick J. Wong } 1926f9703d0SDarrick J. Wong 193ef67959cSChris Packham static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned int reg, 194ef67959cSChris Packham unsigned int val) 1956f9703d0SDarrick J. Wong { 196ef67959cSChris Packham u8 regval[2]; 197ef67959cSChris Packham 198ef67959cSChris Packham regval[0] = val & 0xFF; 199ef67959cSChris Packham regval[1] = val >> 8; 200ef67959cSChris Packham 201ef67959cSChris Packham return regmap_bulk_write(data->regmap, reg, ®val, 2); 2026f9703d0SDarrick J. Wong } 2036f9703d0SDarrick J. Wong 20489fac11cSDarrick J. Wong /* Probe for temperature sensors. Assumes lock is held */ 205ef67959cSChris Packham static int adt7470_read_temperatures(struct adt7470_data *data) 2066f9703d0SDarrick J. Wong { 20789fac11cSDarrick J. Wong unsigned long res; 208ef67959cSChris Packham unsigned int pwm_cfg[2]; 209ef67959cSChris Packham int err; 2106f9703d0SDarrick J. Wong int i; 211ef67959cSChris Packham u8 pwm[ADT7470_FAN_COUNT]; 2126f9703d0SDarrick J. Wong 2132e75a4b7SDarrick J. Wong /* save pwm[1-4] config register */ 214ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); 215ef67959cSChris Packham if (err < 0) 216ef67959cSChris Packham return err; 217ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(2), &pwm_cfg[1]); 218ef67959cSChris Packham if (err < 0) 219ef67959cSChris Packham return err; 2202e75a4b7SDarrick J. Wong 2212e75a4b7SDarrick J. Wong /* set manual pwm to whatever it is set to now */ 222ef67959cSChris Packham err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &pwm[0], 223ef67959cSChris Packham ADT7470_PWM_COUNT); 224ef67959cSChris Packham if (err < 0) 225ef67959cSChris Packham return err; 2262e75a4b7SDarrick J. Wong 2272e75a4b7SDarrick J. Wong /* put pwm in manual mode */ 228ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(0), 229ef67959cSChris Packham ADT7470_PWM_AUTO_MASK, 0); 230ef67959cSChris Packham if (err < 0) 231ef67959cSChris Packham return err; 232ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), 233ef67959cSChris Packham ADT7470_PWM_AUTO_MASK, 0); 234ef67959cSChris Packham if (err < 0) 235ef67959cSChris Packham return err; 2362e75a4b7SDarrick J. Wong 2372e75a4b7SDarrick J. Wong /* write pwm control to whatever it was */ 238ef67959cSChris Packham err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], 239ef67959cSChris Packham ADT7470_PWM_COUNT); 240ef67959cSChris Packham if (err < 0) 241ef67959cSChris Packham return err; 2422e75a4b7SDarrick J. Wong 2436f9703d0SDarrick J. Wong /* start reading temperature sensors */ 244ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, 245ef67959cSChris Packham ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); 246ef67959cSChris Packham if (err < 0) 247ef67959cSChris Packham return err; 2486f9703d0SDarrick J. Wong 2492f22d5dfSDarrick J. Wong /* Delay is 200ms * number of temp sensors. */ 25089fac11cSDarrick J. Wong res = msleep_interruptible((data->num_temp_sensors >= 0 ? 2512f22d5dfSDarrick J. Wong data->num_temp_sensors * 200 : 2522f22d5dfSDarrick J. Wong TEMP_COLLECTION_TIME)); 2536f9703d0SDarrick J. Wong 2546f9703d0SDarrick J. Wong /* done reading temperature sensors */ 255ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, 256ef67959cSChris Packham ADT7470_T05_STB_MASK, 0); 257ef67959cSChris Packham if (err < 0) 258ef67959cSChris Packham return err; 2596f9703d0SDarrick J. Wong 2602e75a4b7SDarrick J. Wong /* restore pwm[1-4] config registers */ 261ef67959cSChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); 262ef67959cSChris Packham if (err < 0) 263ef67959cSChris Packham return err; 264ef67959cSChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); 265ef67959cSChris Packham if (err < 0) 266ef67959cSChris Packham return err; 2672e75a4b7SDarrick J. Wong 268ef67959cSChris Packham if (res) 26989fac11cSDarrick J. Wong return -EAGAIN; 27089fac11cSDarrick J. Wong 27189fac11cSDarrick J. Wong /* Only count fans if we have to */ 27289fac11cSDarrick J. Wong if (data->num_temp_sensors >= 0) 27389fac11cSDarrick J. Wong return 0; 27489fac11cSDarrick J. Wong 275ef67959cSChris Packham err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0], 276ef67959cSChris Packham ADT7470_TEMP_COUNT); 277ef67959cSChris Packham if (err < 0) 278ef67959cSChris Packham return err; 27989fac11cSDarrick J. Wong for (i = 0; i < ADT7470_TEMP_COUNT; i++) { 28089fac11cSDarrick J. Wong if (data->temp[i]) 28189fac11cSDarrick J. Wong data->num_temp_sensors = i + 1; 28289fac11cSDarrick J. Wong } 28389fac11cSDarrick J. Wong data->temperatures_probed = 1; 28489fac11cSDarrick J. Wong return 0; 28589fac11cSDarrick J. Wong } 28689fac11cSDarrick J. Wong 28789fac11cSDarrick J. Wong static int adt7470_update_thread(void *p) 28889fac11cSDarrick J. Wong { 28989fac11cSDarrick J. Wong struct i2c_client *client = p; 29089fac11cSDarrick J. Wong struct adt7470_data *data = i2c_get_clientdata(client); 29189fac11cSDarrick J. Wong 29289fac11cSDarrick J. Wong while (!kthread_should_stop()) { 29389fac11cSDarrick J. Wong mutex_lock(&data->lock); 294ef67959cSChris Packham adt7470_read_temperatures(data); 29589fac11cSDarrick J. Wong mutex_unlock(&data->lock); 29693cacfd4SJoshua Scott 29793cacfd4SJoshua Scott set_current_state(TASK_INTERRUPTIBLE); 29889fac11cSDarrick J. Wong if (kthread_should_stop()) 29989fac11cSDarrick J. Wong break; 30093cacfd4SJoshua Scott 30193cacfd4SJoshua Scott schedule_timeout(msecs_to_jiffies(data->auto_update_interval)); 30289fac11cSDarrick J. Wong } 30389fac11cSDarrick J. Wong 30489fac11cSDarrick J. Wong return 0; 30589fac11cSDarrick J. Wong } 30689fac11cSDarrick J. Wong 307ad00a02eSChris Packham static int adt7470_update_sensors(struct adt7470_data *data) 30889fac11cSDarrick J. Wong { 309ef67959cSChris Packham unsigned int val; 310ef67959cSChris Packham int err; 31189fac11cSDarrick J. Wong int i; 31289fac11cSDarrick J. Wong 31389fac11cSDarrick J. Wong if (!data->temperatures_probed) 314ef67959cSChris Packham err = adt7470_read_temperatures(data); 31589fac11cSDarrick J. Wong else 316ef67959cSChris Packham err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0], 317ef67959cSChris Packham ADT7470_TEMP_COUNT); 318ef67959cSChris Packham if (err < 0) 319ef67959cSChris Packham return err; 3206f9703d0SDarrick J. Wong 321ef67959cSChris Packham for (i = 0; i < ADT7470_FAN_COUNT; i++) { 322ef67959cSChris Packham err = adt7470_read_word_data(data, ADT7470_REG_FAN(i), &val); 323ef67959cSChris Packham if (err < 0) 324ef67959cSChris Packham return err; 325ef67959cSChris Packham data->fan[i] = val; 3266f9703d0SDarrick J. Wong } 3276f9703d0SDarrick J. Wong 328ef67959cSChris Packham err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &data->pwm[0], ADT7470_PWM_COUNT); 329ef67959cSChris Packham if (err < 0) 330ef67959cSChris Packham return err; 3316f9703d0SDarrick J. Wong 332ef67959cSChris Packham for (i = 0; i < ADT7470_PWM_COUNT; i++) { 333ef67959cSChris Packham unsigned int mask; 334ef67959cSChris Packham 335ef67959cSChris Packham if (i % 2) 336ef67959cSChris Packham mask = ADT7470_PWM2_AUTO_MASK; 337ef67959cSChris Packham else 338ef67959cSChris Packham mask = ADT7470_PWM1_AUTO_MASK; 339ef67959cSChris Packham 340ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(i), &val); 341ef67959cSChris Packham if (err < 0) 342ef67959cSChris Packham return err; 343ef67959cSChris Packham data->pwm_automatic[i] = !!(val & mask); 344ef67959cSChris Packham 345ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_PWM_AUTO_TEMP(i), &val); 346ef67959cSChris Packham if (err < 0) 347ef67959cSChris Packham return err; 348ef67959cSChris Packham if (!(i % 2)) 349ef67959cSChris Packham data->pwm_auto_temp[i] = val >> 4; 350ef67959cSChris Packham else 351ef67959cSChris Packham data->pwm_auto_temp[i] = val & 0xF; 352ef67959cSChris Packham } 353ef67959cSChris Packham 354ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_CFG, &val); 355ef67959cSChris Packham if (err < 0) 356ef67959cSChris Packham return err; 357ef67959cSChris Packham data->force_pwm_max = !!(val & ADT7470_FSPD_MASK); 358ef67959cSChris Packham 359ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_ALARM1, &val); 360ef67959cSChris Packham if (err < 0) 361ef67959cSChris Packham return err; 362ef67959cSChris Packham data->alarm = val; 363ef67959cSChris Packham if (data->alarm & ADT7470_OOL_ALARM) { 364ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_ALARM2, &val); 365ef67959cSChris Packham if (err < 0) 366ef67959cSChris Packham return err; 367ef67959cSChris Packham data->alarm |= ALARM2(val); 368ef67959cSChris Packham } 369ef67959cSChris Packham 370ef67959cSChris Packham err = adt7470_read_word_data(data, ADT7470_REG_ALARM1_MASK, &val); 371ef67959cSChris Packham if (err < 0) 372ef67959cSChris Packham return err; 373ef67959cSChris Packham data->alarms_mask = val; 3746f9703d0SDarrick J. Wong 375ad00a02eSChris Packham return 0; 376ad00a02eSChris Packham } 3776f9703d0SDarrick J. Wong 378ad00a02eSChris Packham static int adt7470_update_limits(struct adt7470_data *data) 379ad00a02eSChris Packham { 380ef67959cSChris Packham unsigned int val; 381ef67959cSChris Packham int err; 382ad00a02eSChris Packham int i; 3836f9703d0SDarrick J. Wong 3846f9703d0SDarrick J. Wong for (i = 0; i < ADT7470_TEMP_COUNT; i++) { 385ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_TEMP_MIN_REG(i), &val); 386ef67959cSChris Packham if (err < 0) 387ef67959cSChris Packham return err; 388ef67959cSChris Packham data->temp_min[i] = (s8)val; 389ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_TEMP_MAX_REG(i), &val); 390ef67959cSChris Packham if (err < 0) 391ef67959cSChris Packham return err; 392ef67959cSChris Packham data->temp_max[i] = (s8)val; 3936f9703d0SDarrick J. Wong } 3946f9703d0SDarrick J. Wong 3956f9703d0SDarrick J. Wong for (i = 0; i < ADT7470_FAN_COUNT; i++) { 396ef67959cSChris Packham err = adt7470_read_word_data(data, ADT7470_REG_FAN_MIN(i), &val); 397ef67959cSChris Packham if (err < 0) 398ef67959cSChris Packham return err; 399ef67959cSChris Packham data->fan_min[i] = val; 400ef67959cSChris Packham err = adt7470_read_word_data(data, ADT7470_REG_FAN_MAX(i), &val); 401ef67959cSChris Packham if (err < 0) 402ef67959cSChris Packham return err; 403ef67959cSChris Packham data->fan_max[i] = val; 4046f9703d0SDarrick J. Wong } 4056f9703d0SDarrick J. Wong 4066f9703d0SDarrick J. Wong for (i = 0; i < ADT7470_PWM_COUNT; i++) { 407ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_PWM_MAX(i), &val); 408ef67959cSChris Packham if (err < 0) 409ef67959cSChris Packham return err; 410ef67959cSChris Packham data->pwm_max[i] = val; 411ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_PWM_MIN(i), &val); 412ef67959cSChris Packham if (err < 0) 413ef67959cSChris Packham return err; 414ef67959cSChris Packham data->pwm_min[i] = val; 415ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_PWM_TMIN(i), &val); 416ef67959cSChris Packham if (err < 0) 417ef67959cSChris Packham return err; 418ef67959cSChris Packham data->pwm_tmin[i] = (s8)val; 4196f9703d0SDarrick J. Wong } 4206f9703d0SDarrick J. Wong 421ad00a02eSChris Packham return 0; 422ad00a02eSChris Packham } 423ad00a02eSChris Packham 424ad00a02eSChris Packham static struct adt7470_data *adt7470_update_device(struct device *dev) 425ad00a02eSChris Packham { 426ad00a02eSChris Packham struct adt7470_data *data = dev_get_drvdata(dev); 427ad00a02eSChris Packham unsigned long local_jiffies = jiffies; 428ad00a02eSChris Packham int need_sensors = 1; 429ad00a02eSChris Packham int need_limits = 1; 430ad00a02eSChris Packham int err; 431ad00a02eSChris Packham 432ad00a02eSChris Packham /* 433ad00a02eSChris Packham * Figure out if we need to update the shadow registers. 434ad00a02eSChris Packham * Lockless means that we may occasionally report out of 435ad00a02eSChris Packham * date data. 436ad00a02eSChris Packham */ 437ad00a02eSChris Packham if (time_before(local_jiffies, data->sensors_last_updated + 438ad00a02eSChris Packham SENSOR_REFRESH_INTERVAL) && 439ad00a02eSChris Packham data->sensors_valid) 440ad00a02eSChris Packham need_sensors = 0; 441ad00a02eSChris Packham 442ad00a02eSChris Packham if (time_before(local_jiffies, data->limits_last_updated + 443ad00a02eSChris Packham LIMIT_REFRESH_INTERVAL) && 444ad00a02eSChris Packham data->limits_valid) 445ad00a02eSChris Packham need_limits = 0; 446ad00a02eSChris Packham 447ad00a02eSChris Packham if (!need_sensors && !need_limits) 448ad00a02eSChris Packham return data; 449ad00a02eSChris Packham 450ad00a02eSChris Packham mutex_lock(&data->lock); 451ad00a02eSChris Packham if (need_sensors) { 452ad00a02eSChris Packham err = adt7470_update_sensors(data); 453ad00a02eSChris Packham if (err < 0) 454ad00a02eSChris Packham goto out; 455ad00a02eSChris Packham data->sensors_last_updated = local_jiffies; 456ad00a02eSChris Packham data->sensors_valid = 1; 457ad00a02eSChris Packham } 458ad00a02eSChris Packham 459ad00a02eSChris Packham if (need_limits) { 460ad00a02eSChris Packham err = adt7470_update_limits(data); 461ad00a02eSChris Packham if (err < 0) 462ad00a02eSChris Packham goto out; 4636f9703d0SDarrick J. Wong data->limits_last_updated = local_jiffies; 4646f9703d0SDarrick J. Wong data->limits_valid = 1; 465ad00a02eSChris Packham } 4666f9703d0SDarrick J. Wong out: 4676f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 468ad00a02eSChris Packham 469ad00a02eSChris Packham return err < 0 ? ERR_PTR(err) : data; 4706f9703d0SDarrick J. Wong } 4716f9703d0SDarrick J. Wong 472808fc6c2SJulia Lawall static ssize_t auto_update_interval_show(struct device *dev, 47389fac11cSDarrick J. Wong struct device_attribute *devattr, 47489fac11cSDarrick J. Wong char *buf) 47589fac11cSDarrick J. Wong { 47689fac11cSDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 477ad00a02eSChris Packham 478ad00a02eSChris Packham if (IS_ERR(data)) 479ad00a02eSChris Packham return PTR_ERR(data); 480ad00a02eSChris Packham 48189fac11cSDarrick J. Wong return sprintf(buf, "%d\n", data->auto_update_interval); 48289fac11cSDarrick J. Wong } 48389fac11cSDarrick J. Wong 484808fc6c2SJulia Lawall static ssize_t auto_update_interval_store(struct device *dev, 48589fac11cSDarrick J. Wong struct device_attribute *devattr, 486808fc6c2SJulia Lawall const char *buf, size_t count) 48789fac11cSDarrick J. Wong { 48830485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 48989fac11cSDarrick J. Wong long temp; 49089fac11cSDarrick J. Wong 491179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 49289fac11cSDarrick J. Wong return -EINVAL; 49389fac11cSDarrick J. Wong 4942a844c14SGuenter Roeck temp = clamp_val(temp, 0, 60000); 49589fac11cSDarrick J. Wong 49689fac11cSDarrick J. Wong mutex_lock(&data->lock); 49789fac11cSDarrick J. Wong data->auto_update_interval = temp; 49889fac11cSDarrick J. Wong mutex_unlock(&data->lock); 49989fac11cSDarrick J. Wong 50089fac11cSDarrick J. Wong return count; 50189fac11cSDarrick J. Wong } 50289fac11cSDarrick J. Wong 503808fc6c2SJulia Lawall static ssize_t num_temp_sensors_show(struct device *dev, 5042f22d5dfSDarrick J. Wong struct device_attribute *devattr, 5052f22d5dfSDarrick J. Wong char *buf) 5062f22d5dfSDarrick J. Wong { 5072f22d5dfSDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 508ad00a02eSChris Packham 509ad00a02eSChris Packham if (IS_ERR(data)) 510ad00a02eSChris Packham return PTR_ERR(data); 511ad00a02eSChris Packham 5122f22d5dfSDarrick J. Wong return sprintf(buf, "%d\n", data->num_temp_sensors); 5132f22d5dfSDarrick J. Wong } 5142f22d5dfSDarrick J. Wong 515808fc6c2SJulia Lawall static ssize_t num_temp_sensors_store(struct device *dev, 5162f22d5dfSDarrick J. Wong struct device_attribute *devattr, 517808fc6c2SJulia Lawall const char *buf, size_t count) 5182f22d5dfSDarrick J. Wong { 51930485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 5202f22d5dfSDarrick J. Wong long temp; 5212f22d5dfSDarrick J. Wong 522179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 5232f22d5dfSDarrick J. Wong return -EINVAL; 5242f22d5dfSDarrick J. Wong 5252a844c14SGuenter Roeck temp = clamp_val(temp, -1, 10); 5262f22d5dfSDarrick J. Wong 5272f22d5dfSDarrick J. Wong mutex_lock(&data->lock); 5282f22d5dfSDarrick J. Wong data->num_temp_sensors = temp; 52989fac11cSDarrick J. Wong if (temp < 0) 53089fac11cSDarrick J. Wong data->temperatures_probed = 0; 5312f22d5dfSDarrick J. Wong mutex_unlock(&data->lock); 5322f22d5dfSDarrick J. Wong 5332f22d5dfSDarrick J. Wong return count; 5342f22d5dfSDarrick J. Wong } 5352f22d5dfSDarrick J. Wong 536*fc958a61SChris Packham static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *val) 5376f9703d0SDarrick J. Wong { 5386f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 539ad00a02eSChris Packham 540ad00a02eSChris Packham if (IS_ERR(data)) 541ad00a02eSChris Packham return PTR_ERR(data); 542ad00a02eSChris Packham 543*fc958a61SChris Packham switch (attr) { 544*fc958a61SChris Packham case hwmon_temp_input: 545*fc958a61SChris Packham *val = 1000 * data->temp[channel]; 546*fc958a61SChris Packham break; 547*fc958a61SChris Packham case hwmon_temp_min: 548*fc958a61SChris Packham *val = 1000 * data->temp_min[channel]; 549*fc958a61SChris Packham break; 550*fc958a61SChris Packham case hwmon_temp_max: 551*fc958a61SChris Packham *val = 1000 * data->temp_max[channel]; 552*fc958a61SChris Packham break; 553*fc958a61SChris Packham case hwmon_temp_alarm: 554*fc958a61SChris Packham *val = !!(data->alarm & channel); 555*fc958a61SChris Packham break; 556*fc958a61SChris Packham default: 557*fc958a61SChris Packham return -EOPNOTSUPP; 5586f9703d0SDarrick J. Wong } 5596f9703d0SDarrick J. Wong 560*fc958a61SChris Packham return 0; 561*fc958a61SChris Packham } 562*fc958a61SChris Packham 563*fc958a61SChris Packham static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long val) 5646f9703d0SDarrick J. Wong { 56530485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 566ef67959cSChris Packham int err; 56705a9bd46SDarrick J. Wong 568*fc958a61SChris Packham val = clamp_val(val, -128000, 127000); 569*fc958a61SChris Packham val = DIV_ROUND_CLOSEST(val, 1000); 57005a9bd46SDarrick J. Wong 571*fc958a61SChris Packham switch (attr) { 572*fc958a61SChris Packham case hwmon_temp_min: 5736f9703d0SDarrick J. Wong mutex_lock(&data->lock); 574*fc958a61SChris Packham data->temp_min[channel] = val; 575*fc958a61SChris Packham err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); 5766f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 577*fc958a61SChris Packham break; 578*fc958a61SChris Packham case hwmon_temp_max: 5796f9703d0SDarrick J. Wong mutex_lock(&data->lock); 580*fc958a61SChris Packham data->temp_max[channel] = val; 581*fc958a61SChris Packham err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); 5826f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 583*fc958a61SChris Packham break; 584*fc958a61SChris Packham default: 585*fc958a61SChris Packham return -EOPNOTSUPP; 5866f9703d0SDarrick J. Wong } 5876f9703d0SDarrick J. Wong 588*fc958a61SChris Packham return err; 5896f9703d0SDarrick J. Wong } 5906f9703d0SDarrick J. Wong 591808fc6c2SJulia Lawall static ssize_t alarm_mask_show(struct device *dev, 592*fc958a61SChris Packham struct device_attribute *devattr, char *buf) 5936f9703d0SDarrick J. Wong { 5946f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 5956f9703d0SDarrick J. Wong 596ad00a02eSChris Packham if (IS_ERR(data)) 597ad00a02eSChris Packham return PTR_ERR(data); 598ad00a02eSChris Packham 5996f9703d0SDarrick J. Wong return sprintf(buf, "%x\n", data->alarms_mask); 6006f9703d0SDarrick J. Wong } 6016f9703d0SDarrick J. Wong 602808fc6c2SJulia Lawall static ssize_t alarm_mask_store(struct device *dev, 603feca3132SJoshua Scott struct device_attribute *devattr, 604808fc6c2SJulia Lawall const char *buf, size_t count) 605feca3132SJoshua Scott { 606feca3132SJoshua Scott struct adt7470_data *data = dev_get_drvdata(dev); 607feca3132SJoshua Scott long mask; 608ef67959cSChris Packham int err; 609feca3132SJoshua Scott 610feca3132SJoshua Scott if (kstrtoul(buf, 0, &mask)) 611feca3132SJoshua Scott return -EINVAL; 612feca3132SJoshua Scott 613feca3132SJoshua Scott if (mask & ~0xffff) 614feca3132SJoshua Scott return -EINVAL; 615feca3132SJoshua Scott 616feca3132SJoshua Scott mutex_lock(&data->lock); 617feca3132SJoshua Scott data->alarms_mask = mask; 618ef67959cSChris Packham err = adt7470_write_word_data(data, ADT7470_REG_ALARM1_MASK, mask); 619feca3132SJoshua Scott mutex_unlock(&data->lock); 620feca3132SJoshua Scott 621ef67959cSChris Packham return err < 0 ? err : count; 622feca3132SJoshua Scott } 623feca3132SJoshua Scott 624*fc958a61SChris Packham static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) 6256f9703d0SDarrick J. Wong { 6266f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 6276f9703d0SDarrick J. Wong 628ad00a02eSChris Packham if (IS_ERR(data)) 629ad00a02eSChris Packham return PTR_ERR(data); 630ad00a02eSChris Packham 631*fc958a61SChris Packham switch (attr) { 632*fc958a61SChris Packham case hwmon_fan_input: 633*fc958a61SChris Packham if (FAN_DATA_VALID(data->fan[channel])) 634*fc958a61SChris Packham *val = FAN_PERIOD_TO_RPM(data->fan[channel]); 6356f9703d0SDarrick J. Wong else 636*fc958a61SChris Packham *val = 0; 637*fc958a61SChris Packham break; 638*fc958a61SChris Packham case hwmon_fan_min: 639*fc958a61SChris Packham if (FAN_DATA_VALID(data->fan_min[channel])) 640*fc958a61SChris Packham *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); 641*fc958a61SChris Packham else 642*fc958a61SChris Packham *val = 0; 643*fc958a61SChris Packham break; 644*fc958a61SChris Packham case hwmon_fan_max: 645*fc958a61SChris Packham if (FAN_DATA_VALID(data->fan_max[channel])) 646*fc958a61SChris Packham *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); 647*fc958a61SChris Packham else 648*fc958a61SChris Packham *val = 0; 649*fc958a61SChris Packham break; 650*fc958a61SChris Packham case hwmon_fan_alarm: 651*fc958a61SChris Packham *val = !!(data->alarm & (1 << (12 + channel))); 652*fc958a61SChris Packham break; 653*fc958a61SChris Packham default: 654*fc958a61SChris Packham return -EOPNOTSUPP; 6556f9703d0SDarrick J. Wong } 6566f9703d0SDarrick J. Wong 657*fc958a61SChris Packham return 0; 658*fc958a61SChris Packham } 659*fc958a61SChris Packham 660*fc958a61SChris Packham static int adt7470_fan_write(struct device *dev, u32 attr, int channel, long val) 6616f9703d0SDarrick J. Wong { 66230485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 663ef67959cSChris Packham int err; 6646f9703d0SDarrick J. Wong 665*fc958a61SChris Packham val = FAN_RPM_TO_PERIOD(val); 666*fc958a61SChris Packham val = clamp_val(val, 1, 65534); 66705a9bd46SDarrick J. Wong 668*fc958a61SChris Packham switch (attr) { 669*fc958a61SChris Packham case hwmon_fan_min: 6706f9703d0SDarrick J. Wong mutex_lock(&data->lock); 671*fc958a61SChris Packham data->fan_min[channel] = val; 672*fc958a61SChris Packham err = adt7470_write_word_data(data, ADT7470_REG_FAN_MIN(channel), val); 6736f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 674*fc958a61SChris Packham break; 675*fc958a61SChris Packham case hwmon_fan_max: 6766f9703d0SDarrick J. Wong mutex_lock(&data->lock); 677*fc958a61SChris Packham data->fan_max[channel] = val; 678*fc958a61SChris Packham err = adt7470_write_word_data(data, ADT7470_REG_FAN_MAX(channel), val); 6796f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 680*fc958a61SChris Packham break; 681*fc958a61SChris Packham default: 682*fc958a61SChris Packham return -EOPNOTSUPP; 6836f9703d0SDarrick J. Wong } 6846f9703d0SDarrick J. Wong 685*fc958a61SChris Packham return err; 6866f9703d0SDarrick J. Wong } 6876f9703d0SDarrick J. Wong 68842291a5aSGuenter Roeck static ssize_t force_pwm_max_show(struct device *dev, 68942291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 6906f9703d0SDarrick J. Wong { 6916f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 692ad00a02eSChris Packham 693ad00a02eSChris Packham if (IS_ERR(data)) 694ad00a02eSChris Packham return PTR_ERR(data); 695ad00a02eSChris Packham 6966f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", data->force_pwm_max); 6976f9703d0SDarrick J. Wong } 6986f9703d0SDarrick J. Wong 69942291a5aSGuenter Roeck static ssize_t force_pwm_max_store(struct device *dev, 7006f9703d0SDarrick J. Wong struct device_attribute *devattr, 70142291a5aSGuenter Roeck const char *buf, size_t count) 7026f9703d0SDarrick J. Wong { 70330485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 70405a9bd46SDarrick J. Wong long temp; 705ef67959cSChris Packham int err; 7066f9703d0SDarrick J. Wong 707179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 70805a9bd46SDarrick J. Wong return -EINVAL; 70905a9bd46SDarrick J. Wong 7106f9703d0SDarrick J. Wong mutex_lock(&data->lock); 7116f9703d0SDarrick J. Wong data->force_pwm_max = temp; 712ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, 713ef67959cSChris Packham ADT7470_FSPD_MASK, 714ef67959cSChris Packham temp ? ADT7470_FSPD_MASK : 0); 7156f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 7166f9703d0SDarrick J. Wong 717ef67959cSChris Packham return err < 0 ? err : count; 7186f9703d0SDarrick J. Wong } 7196f9703d0SDarrick J. Wong 720aa18cc91SJoshua Scott /* These are the valid PWM frequencies to the nearest Hz */ 721aa18cc91SJoshua Scott static const int adt7470_freq_map[] = { 722aa18cc91SJoshua Scott 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 723aa18cc91SJoshua Scott }; 724aa18cc91SJoshua Scott 725*fc958a61SChris Packham static int pwm1_freq_get(struct device *dev) 726aa18cc91SJoshua Scott { 727*fc958a61SChris Packham struct adt7470_data *data = dev_get_drvdata(dev); 728ef67959cSChris Packham unsigned int cfg_reg_1, cfg_reg_2; 729aa18cc91SJoshua Scott int index; 730ef67959cSChris Packham int err; 731ef67959cSChris Packham 732aa18cc91SJoshua Scott mutex_lock(&data->lock); 733ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1); 734ef67959cSChris Packham if (err < 0) 735ef67959cSChris Packham goto out; 736ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2); 737ef67959cSChris Packham if (err < 0) 738ef67959cSChris Packham goto out; 739aa18cc91SJoshua Scott mutex_unlock(&data->lock); 740aa18cc91SJoshua Scott 741aa18cc91SJoshua Scott index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; 742aa18cc91SJoshua Scott if (!(cfg_reg_1 & ADT7470_CFG_LF)) 743aa18cc91SJoshua Scott index += 8; 744aa18cc91SJoshua Scott if (index >= ARRAY_SIZE(adt7470_freq_map)) 745aa18cc91SJoshua Scott index = ARRAY_SIZE(adt7470_freq_map) - 1; 746aa18cc91SJoshua Scott 747*fc958a61SChris Packham return adt7470_freq_map[index]; 748ef67959cSChris Packham 749ef67959cSChris Packham out: 750ef67959cSChris Packham mutex_unlock(&data->lock); 751ef67959cSChris Packham return err; 752aa18cc91SJoshua Scott } 753aa18cc91SJoshua Scott 754*fc958a61SChris Packham static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val) 755*fc958a61SChris Packham { 756*fc958a61SChris Packham struct adt7470_data *data = adt7470_update_device(dev); 757*fc958a61SChris Packham 758*fc958a61SChris Packham if (IS_ERR(data)) 759*fc958a61SChris Packham return PTR_ERR(data); 760*fc958a61SChris Packham 761*fc958a61SChris Packham switch (attr) { 762*fc958a61SChris Packham case hwmon_pwm_input: 763*fc958a61SChris Packham *val = data->pwm[channel]; 764*fc958a61SChris Packham break; 765*fc958a61SChris Packham case hwmon_pwm_enable: 766*fc958a61SChris Packham *val = 1 + data->pwm_automatic[channel]; 767*fc958a61SChris Packham break; 768*fc958a61SChris Packham case hwmon_pwm_freq: 769*fc958a61SChris Packham *val = pwm1_freq_get(dev); 770*fc958a61SChris Packham break; 771*fc958a61SChris Packham default: 772*fc958a61SChris Packham return -EOPNOTSUPP; 773*fc958a61SChris Packham } 774*fc958a61SChris Packham 775*fc958a61SChris Packham return 0; 776*fc958a61SChris Packham } 777*fc958a61SChris Packham 778*fc958a61SChris Packham static int pwm1_freq_set(struct device *dev, long freq) 779aa18cc91SJoshua Scott { 780aa18cc91SJoshua Scott struct adt7470_data *data = dev_get_drvdata(dev); 781*fc958a61SChris Packham unsigned int low_freq = ADT7470_CFG_LF; 782aa18cc91SJoshua Scott int index; 783ef67959cSChris Packham int err; 784aa18cc91SJoshua Scott 785aa18cc91SJoshua Scott /* Round the user value given to the closest available frequency */ 786aa18cc91SJoshua Scott index = find_closest(freq, adt7470_freq_map, 787aa18cc91SJoshua Scott ARRAY_SIZE(adt7470_freq_map)); 788aa18cc91SJoshua Scott 789aa18cc91SJoshua Scott if (index >= 8) { 790aa18cc91SJoshua Scott index -= 8; 791aa18cc91SJoshua Scott low_freq = 0; 792aa18cc91SJoshua Scott } 793aa18cc91SJoshua Scott 794aa18cc91SJoshua Scott mutex_lock(&data->lock); 795aa18cc91SJoshua Scott /* Configuration Register 1 */ 796ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, 797ef67959cSChris Packham ADT7470_CFG_LF, low_freq); 798ef67959cSChris Packham if (err < 0) 799ef67959cSChris Packham goto out; 800ef67959cSChris Packham 801aa18cc91SJoshua Scott /* Configuration Register 2 */ 802ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, 803ef67959cSChris Packham ADT7470_FREQ_MASK, 804ef67959cSChris Packham index << ADT7470_FREQ_SHIFT); 805ef67959cSChris Packham out: 806aa18cc91SJoshua Scott mutex_unlock(&data->lock); 807aa18cc91SJoshua Scott 808*fc958a61SChris Packham return err; 809*fc958a61SChris Packham } 810*fc958a61SChris Packham 811*fc958a61SChris Packham static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val) 812*fc958a61SChris Packham { 813*fc958a61SChris Packham struct adt7470_data *data = dev_get_drvdata(dev); 814*fc958a61SChris Packham unsigned int pwm_auto_reg_mask; 815*fc958a61SChris Packham int err; 816*fc958a61SChris Packham 817*fc958a61SChris Packham switch (attr) { 818*fc958a61SChris Packham case hwmon_pwm_input: 819*fc958a61SChris Packham val = clamp_val(val, 0, 255); 820*fc958a61SChris Packham mutex_lock(&data->lock); 821*fc958a61SChris Packham data->pwm[channel] = val; 822*fc958a61SChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), 823*fc958a61SChris Packham data->pwm[channel]); 824*fc958a61SChris Packham mutex_unlock(&data->lock); 825*fc958a61SChris Packham break; 826*fc958a61SChris Packham case hwmon_pwm_enable: 827*fc958a61SChris Packham if (channel % 2) 828*fc958a61SChris Packham pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; 829*fc958a61SChris Packham else 830*fc958a61SChris Packham pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK; 831*fc958a61SChris Packham 832*fc958a61SChris Packham if (val != 2 && val != 1) 833*fc958a61SChris Packham return -EINVAL; 834*fc958a61SChris Packham val--; 835*fc958a61SChris Packham 836*fc958a61SChris Packham mutex_lock(&data->lock); 837*fc958a61SChris Packham data->pwm_automatic[channel] = val; 838*fc958a61SChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), 839*fc958a61SChris Packham pwm_auto_reg_mask, 840*fc958a61SChris Packham val ? pwm_auto_reg_mask : 0); 841*fc958a61SChris Packham mutex_unlock(&data->lock); 842*fc958a61SChris Packham break; 843*fc958a61SChris Packham case hwmon_pwm_freq: 844*fc958a61SChris Packham err = pwm1_freq_set(dev, val); 845*fc958a61SChris Packham break; 846*fc958a61SChris Packham default: 847*fc958a61SChris Packham return -EOPNOTSUPP; 848*fc958a61SChris Packham } 849*fc958a61SChris Packham 850*fc958a61SChris Packham return err; 851aa18cc91SJoshua Scott } 852aa18cc91SJoshua Scott 85342291a5aSGuenter Roeck static ssize_t pwm_max_show(struct device *dev, 85442291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 8556f9703d0SDarrick J. Wong { 8566f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 8576f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 858ad00a02eSChris Packham 859ad00a02eSChris Packham if (IS_ERR(data)) 860ad00a02eSChris Packham return PTR_ERR(data); 861ad00a02eSChris Packham 8626f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", data->pwm_max[attr->index]); 8636f9703d0SDarrick J. Wong } 8646f9703d0SDarrick J. Wong 86542291a5aSGuenter Roeck static ssize_t pwm_max_store(struct device *dev, 8666f9703d0SDarrick J. Wong struct device_attribute *devattr, 86742291a5aSGuenter Roeck const char *buf, size_t count) 8686f9703d0SDarrick J. Wong { 8696f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 87030485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 87105a9bd46SDarrick J. Wong long temp; 872ef67959cSChris Packham int err; 87305a9bd46SDarrick J. Wong 874179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 87505a9bd46SDarrick J. Wong return -EINVAL; 87605a9bd46SDarrick J. Wong 8772a844c14SGuenter Roeck temp = clamp_val(temp, 0, 255); 8786f9703d0SDarrick J. Wong 8796f9703d0SDarrick J. Wong mutex_lock(&data->lock); 8806f9703d0SDarrick J. Wong data->pwm_max[attr->index] = temp; 881ef67959cSChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM_MAX(attr->index), 8826f9703d0SDarrick J. Wong temp); 8836f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 8846f9703d0SDarrick J. Wong 885ef67959cSChris Packham return err < 0 ? err : count; 8866f9703d0SDarrick J. Wong } 8876f9703d0SDarrick J. Wong 88842291a5aSGuenter Roeck static ssize_t pwm_min_show(struct device *dev, 88942291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 8906f9703d0SDarrick J. Wong { 8916f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 8926f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 893ad00a02eSChris Packham 894ad00a02eSChris Packham if (IS_ERR(data)) 895ad00a02eSChris Packham return PTR_ERR(data); 896ad00a02eSChris Packham 8976f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", data->pwm_min[attr->index]); 8986f9703d0SDarrick J. Wong } 8996f9703d0SDarrick J. Wong 90042291a5aSGuenter Roeck static ssize_t pwm_min_store(struct device *dev, 9016f9703d0SDarrick J. Wong struct device_attribute *devattr, 90242291a5aSGuenter Roeck const char *buf, size_t count) 9036f9703d0SDarrick J. Wong { 9046f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 90530485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 90605a9bd46SDarrick J. Wong long temp; 907ef67959cSChris Packham int err; 90805a9bd46SDarrick J. Wong 909179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 91005a9bd46SDarrick J. Wong return -EINVAL; 91105a9bd46SDarrick J. Wong 9122a844c14SGuenter Roeck temp = clamp_val(temp, 0, 255); 9136f9703d0SDarrick J. Wong 9146f9703d0SDarrick J. Wong mutex_lock(&data->lock); 9156f9703d0SDarrick J. Wong data->pwm_min[attr->index] = temp; 916ef67959cSChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM_MIN(attr->index), 9176f9703d0SDarrick J. Wong temp); 9186f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 9196f9703d0SDarrick J. Wong 920ef67959cSChris Packham return err < 0 ? err : count; 9216f9703d0SDarrick J. Wong } 9226f9703d0SDarrick J. Wong 92342291a5aSGuenter Roeck static ssize_t pwm_tmax_show(struct device *dev, 92442291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 9256f9703d0SDarrick J. Wong { 9266f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 9276f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 928ad00a02eSChris Packham 929ad00a02eSChris Packham if (IS_ERR(data)) 930ad00a02eSChris Packham return PTR_ERR(data); 931ad00a02eSChris Packham 9326f9703d0SDarrick J. Wong /* the datasheet says that tmax = tmin + 20C */ 9336f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", 1000 * (20 + data->pwm_tmin[attr->index])); 9346f9703d0SDarrick J. Wong } 9356f9703d0SDarrick J. Wong 93642291a5aSGuenter Roeck static ssize_t pwm_tmin_show(struct device *dev, 93742291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 9386f9703d0SDarrick J. Wong { 9396f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 9406f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 941ad00a02eSChris Packham 942ad00a02eSChris Packham if (IS_ERR(data)) 943ad00a02eSChris Packham return PTR_ERR(data); 944ad00a02eSChris Packham 9456f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", 1000 * data->pwm_tmin[attr->index]); 9466f9703d0SDarrick J. Wong } 9476f9703d0SDarrick J. Wong 94842291a5aSGuenter Roeck static ssize_t pwm_tmin_store(struct device *dev, 9496f9703d0SDarrick J. Wong struct device_attribute *devattr, 95042291a5aSGuenter Roeck const char *buf, size_t count) 9516f9703d0SDarrick J. Wong { 9526f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 95330485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 95405a9bd46SDarrick J. Wong long temp; 955ef67959cSChris Packham int err; 95605a9bd46SDarrick J. Wong 957179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 95805a9bd46SDarrick J. Wong return -EINVAL; 95905a9bd46SDarrick J. Wong 96064bd708aSGuenter Roeck temp = clamp_val(temp, -128000, 127000); 9618f8c1fb0SDarrick J. Wong temp = DIV_ROUND_CLOSEST(temp, 1000); 9626f9703d0SDarrick J. Wong 9636f9703d0SDarrick J. Wong mutex_lock(&data->lock); 9646f9703d0SDarrick J. Wong data->pwm_tmin[attr->index] = temp; 965ef67959cSChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM_TMIN(attr->index), 9666f9703d0SDarrick J. Wong temp); 9676f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 9686f9703d0SDarrick J. Wong 969ef67959cSChris Packham return err < 0 ? err : count; 9706f9703d0SDarrick J. Wong } 9716f9703d0SDarrick J. Wong 97242291a5aSGuenter Roeck static ssize_t pwm_auto_temp_show(struct device *dev, 97342291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 9746f9703d0SDarrick J. Wong { 9756f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 9766f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 977ad00a02eSChris Packham u8 ctrl; 9786f9703d0SDarrick J. Wong 979ad00a02eSChris Packham if (IS_ERR(data)) 980ad00a02eSChris Packham return PTR_ERR(data); 981ad00a02eSChris Packham 982ad00a02eSChris Packham ctrl = data->pwm_auto_temp[attr->index]; 9836f9703d0SDarrick J. Wong if (ctrl) 9846f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", 1 << (ctrl - 1)); 9856f9703d0SDarrick J. Wong else 9866f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", ADT7470_PWM_ALL_TEMPS); 9876f9703d0SDarrick J. Wong } 9886f9703d0SDarrick J. Wong 9896f9703d0SDarrick J. Wong static int cvt_auto_temp(int input) 9906f9703d0SDarrick J. Wong { 9916f9703d0SDarrick J. Wong if (input == ADT7470_PWM_ALL_TEMPS) 9926f9703d0SDarrick J. Wong return 0; 993ce9c2f44SRobert P. J. Day if (input < 1 || !is_power_of_2(input)) 9946f9703d0SDarrick J. Wong return -EINVAL; 9956f9703d0SDarrick J. Wong return ilog2(input) + 1; 9966f9703d0SDarrick J. Wong } 9976f9703d0SDarrick J. Wong 99842291a5aSGuenter Roeck static ssize_t pwm_auto_temp_store(struct device *dev, 9996f9703d0SDarrick J. Wong struct device_attribute *devattr, 100042291a5aSGuenter Roeck const char *buf, size_t count) 10016f9703d0SDarrick J. Wong { 10026f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 100330485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 10046f9703d0SDarrick J. Wong int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index); 1005ef67959cSChris Packham unsigned int mask, val; 100605a9bd46SDarrick J. Wong long temp; 1007ef67959cSChris Packham int err; 10086f9703d0SDarrick J. Wong 1009179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 101005a9bd46SDarrick J. Wong return -EINVAL; 101105a9bd46SDarrick J. Wong 101205a9bd46SDarrick J. Wong temp = cvt_auto_temp(temp); 10136f9703d0SDarrick J. Wong if (temp < 0) 10146f9703d0SDarrick J. Wong return temp; 10156f9703d0SDarrick J. Wong 10166f9703d0SDarrick J. Wong mutex_lock(&data->lock); 10176f9703d0SDarrick J. Wong data->pwm_automatic[attr->index] = temp; 10186f9703d0SDarrick J. Wong 10196f9703d0SDarrick J. Wong if (!(attr->index % 2)) { 1020ef67959cSChris Packham mask = 0xF0; 1021ef67959cSChris Packham val = (temp << 4) & 0xF0; 10226f9703d0SDarrick J. Wong } else { 1023ef67959cSChris Packham mask = 0x0F; 1024ef67959cSChris Packham val = temp & 0x0F; 10256f9703d0SDarrick J. Wong } 10266f9703d0SDarrick J. Wong 1027ef67959cSChris Packham err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); 10286f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 10296f9703d0SDarrick J. Wong 1030ef67959cSChris Packham return err < 0 ? err : count; 10316f9703d0SDarrick J. Wong } 10326f9703d0SDarrick J. Wong 1033808fc6c2SJulia Lawall static DEVICE_ATTR_RW(alarm_mask); 1034808fc6c2SJulia Lawall static DEVICE_ATTR_RW(num_temp_sensors); 1035808fc6c2SJulia Lawall static DEVICE_ATTR_RW(auto_update_interval); 10366f9703d0SDarrick J. Wong 103742291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(force_pwm_max, force_pwm_max, 0); 10386f9703d0SDarrick J. Wong 103942291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_pwm, pwm_min, 0); 104042291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_pwm, pwm_min, 1); 104142291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point1_pwm, pwm_min, 2); 104242291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point1_pwm, pwm_min, 3); 10436f9703d0SDarrick J. Wong 104442291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_pwm, pwm_max, 0); 104542291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point2_pwm, pwm_max, 1); 104642291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point2_pwm, pwm_max, 2); 104742291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point2_pwm, pwm_max, 3); 10486f9703d0SDarrick J. Wong 104942291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, pwm_tmin, 0); 105042291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_temp, pwm_tmin, 1); 105142291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point1_temp, pwm_tmin, 2); 105242291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point1_temp, pwm_tmin, 3); 10536f9703d0SDarrick J. Wong 105442291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point2_temp, pwm_tmax, 0); 105542291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(pwm2_auto_point2_temp, pwm_tmax, 1); 105642291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(pwm3_auto_point2_temp, pwm_tmax, 2); 105742291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(pwm4_auto_point2_temp, pwm_tmax, 3); 10586f9703d0SDarrick J. Wong 105942291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm1_auto_channels_temp, pwm_auto_temp, 0); 106042291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm2_auto_channels_temp, pwm_auto_temp, 1); 106142291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm3_auto_channels_temp, pwm_auto_temp, 2); 106242291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm4_auto_channels_temp, pwm_auto_temp, 3); 10636f9703d0SDarrick J. Wong 106430485776SAxel Lin static struct attribute *adt7470_attrs[] = { 1065fe03f28cSDarrick J. Wong &dev_attr_alarm_mask.attr, 10662f22d5dfSDarrick J. Wong &dev_attr_num_temp_sensors.attr, 106789fac11cSDarrick J. Wong &dev_attr_auto_update_interval.attr, 10686f9703d0SDarrick J. Wong &sensor_dev_attr_force_pwm_max.dev_attr.attr, 10696f9703d0SDarrick J. Wong &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, 10706f9703d0SDarrick J. Wong &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, 10716f9703d0SDarrick J. Wong &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, 10726f9703d0SDarrick J. Wong &sensor_dev_attr_pwm4_auto_point1_pwm.dev_attr.attr, 10736f9703d0SDarrick J. Wong &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, 10746f9703d0SDarrick J. Wong &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, 10756f9703d0SDarrick J. Wong &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, 10766f9703d0SDarrick J. Wong &sensor_dev_attr_pwm4_auto_point2_pwm.dev_attr.attr, 10776f9703d0SDarrick J. Wong &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, 10786f9703d0SDarrick J. Wong &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, 10796f9703d0SDarrick J. Wong &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr, 10806f9703d0SDarrick J. Wong &sensor_dev_attr_pwm4_auto_point1_temp.dev_attr.attr, 10816f9703d0SDarrick J. Wong &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, 10826f9703d0SDarrick J. Wong &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, 10836f9703d0SDarrick J. Wong &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, 10846f9703d0SDarrick J. Wong &sensor_dev_attr_pwm4_auto_point2_temp.dev_attr.attr, 10856f9703d0SDarrick J. Wong &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, 10866f9703d0SDarrick J. Wong &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, 10876f9703d0SDarrick J. Wong &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, 10886f9703d0SDarrick J. Wong &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr, 10896f9703d0SDarrick J. Wong NULL 10906f9703d0SDarrick J. Wong }; 10916f9703d0SDarrick J. Wong 109230485776SAxel Lin ATTRIBUTE_GROUPS(adt7470); 109330485776SAxel Lin 1094*fc958a61SChris Packham static int adt7470_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, 1095*fc958a61SChris Packham int channel, long *val) 1096*fc958a61SChris Packham { 1097*fc958a61SChris Packham switch (type) { 1098*fc958a61SChris Packham case hwmon_temp: 1099*fc958a61SChris Packham return adt7470_temp_read(dev, attr, channel, val); 1100*fc958a61SChris Packham case hwmon_fan: 1101*fc958a61SChris Packham return adt7470_fan_read(dev, attr, channel, val); 1102*fc958a61SChris Packham case hwmon_pwm: 1103*fc958a61SChris Packham return adt7470_pwm_read(dev, attr, channel, val); 1104*fc958a61SChris Packham default: 1105*fc958a61SChris Packham return -EOPNOTSUPP; 1106*fc958a61SChris Packham } 1107*fc958a61SChris Packham } 1108*fc958a61SChris Packham 1109*fc958a61SChris Packham static int adt7470_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, 1110*fc958a61SChris Packham int channel, long val) 1111*fc958a61SChris Packham { 1112*fc958a61SChris Packham switch (type) { 1113*fc958a61SChris Packham case hwmon_temp: 1114*fc958a61SChris Packham return adt7470_temp_write(dev, attr, channel, val); 1115*fc958a61SChris Packham case hwmon_fan: 1116*fc958a61SChris Packham return adt7470_fan_write(dev, attr, channel, val); 1117*fc958a61SChris Packham case hwmon_pwm: 1118*fc958a61SChris Packham return adt7470_pwm_write(dev, attr, channel, val); 1119*fc958a61SChris Packham default: 1120*fc958a61SChris Packham return -EOPNOTSUPP; 1121*fc958a61SChris Packham } 1122*fc958a61SChris Packham } 1123*fc958a61SChris Packham 1124*fc958a61SChris Packham static umode_t adt7470_is_visible(const void *_data, enum hwmon_sensor_types type, 1125*fc958a61SChris Packham u32 attr, int channel) 1126*fc958a61SChris Packham { 1127*fc958a61SChris Packham umode_t mode = 0; 1128*fc958a61SChris Packham 1129*fc958a61SChris Packham switch (type) { 1130*fc958a61SChris Packham case hwmon_temp: 1131*fc958a61SChris Packham switch (attr) { 1132*fc958a61SChris Packham case hwmon_temp: 1133*fc958a61SChris Packham case hwmon_temp_alarm: 1134*fc958a61SChris Packham mode = 0444; 1135*fc958a61SChris Packham break; 1136*fc958a61SChris Packham case hwmon_temp_min: 1137*fc958a61SChris Packham case hwmon_temp_max: 1138*fc958a61SChris Packham mode = 0644; 1139*fc958a61SChris Packham break; 1140*fc958a61SChris Packham default: 1141*fc958a61SChris Packham break; 1142*fc958a61SChris Packham } 1143*fc958a61SChris Packham break; 1144*fc958a61SChris Packham case hwmon_fan: 1145*fc958a61SChris Packham switch (attr) { 1146*fc958a61SChris Packham case hwmon_fan_input: 1147*fc958a61SChris Packham case hwmon_fan_alarm: 1148*fc958a61SChris Packham mode = 0444; 1149*fc958a61SChris Packham break; 1150*fc958a61SChris Packham case hwmon_fan_min: 1151*fc958a61SChris Packham case hwmon_fan_max: 1152*fc958a61SChris Packham mode = 0644; 1153*fc958a61SChris Packham break; 1154*fc958a61SChris Packham default: 1155*fc958a61SChris Packham break; 1156*fc958a61SChris Packham } 1157*fc958a61SChris Packham break; 1158*fc958a61SChris Packham case hwmon_pwm: 1159*fc958a61SChris Packham switch (attr) { 1160*fc958a61SChris Packham case hwmon_pwm_input: 1161*fc958a61SChris Packham case hwmon_pwm_enable: 1162*fc958a61SChris Packham mode = 0644; 1163*fc958a61SChris Packham break; 1164*fc958a61SChris Packham case hwmon_pwm_freq: 1165*fc958a61SChris Packham if (channel == 0) 1166*fc958a61SChris Packham mode = 0644; 1167*fc958a61SChris Packham else 1168*fc958a61SChris Packham mode = 0; 1169*fc958a61SChris Packham break; 1170*fc958a61SChris Packham default: 1171*fc958a61SChris Packham break; 1172*fc958a61SChris Packham } 1173*fc958a61SChris Packham break; 1174*fc958a61SChris Packham default: 1175*fc958a61SChris Packham break; 1176*fc958a61SChris Packham } 1177*fc958a61SChris Packham 1178*fc958a61SChris Packham return mode; 1179*fc958a61SChris Packham } 1180*fc958a61SChris Packham 1181*fc958a61SChris Packham static const struct hwmon_ops adt7470_hwmon_ops = { 1182*fc958a61SChris Packham .is_visible = adt7470_is_visible, 1183*fc958a61SChris Packham .read = adt7470_read, 1184*fc958a61SChris Packham .write = adt7470_write, 1185*fc958a61SChris Packham }; 1186*fc958a61SChris Packham 1187*fc958a61SChris Packham static const struct hwmon_channel_info *adt7470_info[] = { 1188*fc958a61SChris Packham HWMON_CHANNEL_INFO(temp, 1189*fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1190*fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1191*fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1192*fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1193*fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1194*fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1195*fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1196*fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1197*fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1198*fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM), 1199*fc958a61SChris Packham HWMON_CHANNEL_INFO(fan, 1200*fc958a61SChris Packham HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, 1201*fc958a61SChris Packham HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, 1202*fc958a61SChris Packham HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, 1203*fc958a61SChris Packham HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM), 1204*fc958a61SChris Packham HWMON_CHANNEL_INFO(pwm, 1205*fc958a61SChris Packham HWMON_PWM_INPUT | HWMON_PWM_ENABLE | HWMON_PWM_FREQ, 1206*fc958a61SChris Packham HWMON_PWM_INPUT | HWMON_PWM_ENABLE, 1207*fc958a61SChris Packham HWMON_PWM_INPUT | HWMON_PWM_ENABLE, 1208*fc958a61SChris Packham HWMON_PWM_INPUT | HWMON_PWM_ENABLE), 1209*fc958a61SChris Packham NULL 1210*fc958a61SChris Packham }; 1211*fc958a61SChris Packham 1212*fc958a61SChris Packham static const struct hwmon_chip_info adt7470_chip_info = { 1213*fc958a61SChris Packham .ops = &adt7470_hwmon_ops, 1214*fc958a61SChris Packham .info = adt7470_info, 1215*fc958a61SChris Packham }; 1216*fc958a61SChris Packham 1217008f1ca5SJean Delvare /* Return 0 if detection is successful, -ENODEV otherwise */ 1218310ec792SJean Delvare static int adt7470_detect(struct i2c_client *client, 1219008f1ca5SJean Delvare struct i2c_board_info *info) 12206f9703d0SDarrick J. Wong { 1221008f1ca5SJean Delvare struct i2c_adapter *adapter = client->adapter; 122252df6440SJean Delvare int vendor, device, revision; 12236f9703d0SDarrick J. Wong 12246f9703d0SDarrick J. Wong if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 1225008f1ca5SJean Delvare return -ENODEV; 12266f9703d0SDarrick J. Wong 12276f9703d0SDarrick J. Wong vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR); 1228008f1ca5SJean Delvare if (vendor != ADT7470_VENDOR) 1229008f1ca5SJean Delvare return -ENODEV; 12306f9703d0SDarrick J. Wong 12316f9703d0SDarrick J. Wong device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE); 1232008f1ca5SJean Delvare if (device != ADT7470_DEVICE) 1233008f1ca5SJean Delvare return -ENODEV; 12346f9703d0SDarrick J. Wong 123552df6440SJean Delvare revision = i2c_smbus_read_byte_data(client, ADT7470_REG_REVISION); 1236008f1ca5SJean Delvare if (revision != ADT7470_REVISION) 1237008f1ca5SJean Delvare return -ENODEV; 12386f9703d0SDarrick J. Wong 123923bd022aSChris Packham strscpy(info->type, "adt7470", I2C_NAME_SIZE); 12406f9703d0SDarrick J. Wong 1241008f1ca5SJean Delvare return 0; 1242008f1ca5SJean Delvare } 1243008f1ca5SJean Delvare 1244ef67959cSChris Packham static const struct regmap_config adt7470_regmap_config = { 1245ef67959cSChris Packham .reg_bits = 8, 1246ef67959cSChris Packham .val_bits = 8, 1247ef67959cSChris Packham .use_single_read = true, 1248ef67959cSChris Packham .use_single_write = true, 1249ef67959cSChris Packham }; 12509027d933SAxel Lin 125167487038SStephen Kitt static int adt7470_probe(struct i2c_client *client) 1252008f1ca5SJean Delvare { 125330485776SAxel Lin struct device *dev = &client->dev; 1254008f1ca5SJean Delvare struct adt7470_data *data; 125530485776SAxel Lin struct device *hwmon_dev; 1256ef67959cSChris Packham int err; 1257008f1ca5SJean Delvare 125830485776SAxel Lin data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); 12599cc7dcc5SGuenter Roeck if (!data) 12609cc7dcc5SGuenter Roeck return -ENOMEM; 1261008f1ca5SJean Delvare 12622f22d5dfSDarrick J. Wong data->num_temp_sensors = -1; 126389fac11cSDarrick J. Wong data->auto_update_interval = AUTO_UPDATE_INTERVAL; 1264ef67959cSChris Packham data->regmap = devm_regmap_init_i2c(client, &adt7470_regmap_config); 1265ef67959cSChris Packham if (IS_ERR(data->regmap)) 1266ef67959cSChris Packham return PTR_ERR(data->regmap); 12672f22d5dfSDarrick J. Wong 1268008f1ca5SJean Delvare i2c_set_clientdata(client, data); 1269008f1ca5SJean Delvare mutex_init(&data->lock); 12706f9703d0SDarrick J. Wong 12716f9703d0SDarrick J. Wong dev_info(&client->dev, "%s chip found\n", client->name); 12726f9703d0SDarrick J. Wong 12736f9703d0SDarrick J. Wong /* Initialize the ADT7470 chip */ 1274ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, 1275ef67959cSChris Packham ADT7470_STRT_MASK | ADT7470_TEST_MASK, 1276ef67959cSChris Packham ADT7470_STRT_MASK | ADT7470_TEST_MASK); 1277ef67959cSChris Packham if (err < 0) 1278ef67959cSChris Packham return err; 12796f9703d0SDarrick J. Wong 12806f9703d0SDarrick J. Wong /* Register sysfs hooks */ 1281*fc958a61SChris Packham hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, 1282*fc958a61SChris Packham &adt7470_chip_info, 128330485776SAxel Lin adt7470_groups); 12846f9703d0SDarrick J. Wong 128530485776SAxel Lin if (IS_ERR(hwmon_dev)) 128630485776SAxel Lin return PTR_ERR(hwmon_dev); 12876f9703d0SDarrick J. Wong 1288f170168bSKees Cook data->auto_update = kthread_run(adt7470_update_thread, client, "%s", 128930485776SAxel Lin dev_name(hwmon_dev)); 129023bd022aSChris Packham if (IS_ERR(data->auto_update)) 129130485776SAxel Lin return PTR_ERR(data->auto_update); 129289fac11cSDarrick J. Wong 12936f9703d0SDarrick J. Wong return 0; 12946f9703d0SDarrick J. Wong } 12956f9703d0SDarrick J. Wong 1296008f1ca5SJean Delvare static int adt7470_remove(struct i2c_client *client) 12976f9703d0SDarrick J. Wong { 12986f9703d0SDarrick J. Wong struct adt7470_data *data = i2c_get_clientdata(client); 12996f9703d0SDarrick J. Wong 130089fac11cSDarrick J. Wong kthread_stop(data->auto_update); 13016f9703d0SDarrick J. Wong return 0; 13026f9703d0SDarrick J. Wong } 13036f9703d0SDarrick J. Wong 13049027d933SAxel Lin static const struct i2c_device_id adt7470_id[] = { 13059027d933SAxel Lin { "adt7470", 0 }, 13069027d933SAxel Lin { } 13079027d933SAxel Lin }; 13089027d933SAxel Lin MODULE_DEVICE_TABLE(i2c, adt7470_id); 13099027d933SAxel Lin 13109027d933SAxel Lin static struct i2c_driver adt7470_driver = { 13119027d933SAxel Lin .class = I2C_CLASS_HWMON, 13129027d933SAxel Lin .driver = { 13139027d933SAxel Lin .name = "adt7470", 13149027d933SAxel Lin }, 131567487038SStephen Kitt .probe_new = adt7470_probe, 13169027d933SAxel Lin .remove = adt7470_remove, 13179027d933SAxel Lin .id_table = adt7470_id, 13189027d933SAxel Lin .detect = adt7470_detect, 13199027d933SAxel Lin .address_list = normal_i2c, 13209027d933SAxel Lin }; 13219027d933SAxel Lin 1322f0967eeaSAxel Lin module_i2c_driver(adt7470_driver); 13236f9703d0SDarrick J. Wong 13245407e051SDarrick J. Wong MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); 13256f9703d0SDarrick J. Wong MODULE_DESCRIPTION("ADT7470 driver"); 13266f9703d0SDarrick J. Wong MODULE_LICENSE("GPL"); 1327