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> 227b2666ceSArmin Wolf #include <linux/sched.h> 235a0e3ad6STejun Heo #include <linux/slab.h> 24aa18cc91SJoshua Scott #include <linux/util_macros.h> 256f9703d0SDarrick J. Wong 266f9703d0SDarrick J. Wong /* Addresses to scan */ 2725e9c86dSMark M. Hoffman static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; 286f9703d0SDarrick J. Wong 296f9703d0SDarrick J. Wong /* ADT7470 registers */ 306f9703d0SDarrick J. Wong #define ADT7470_REG_BASE_ADDR 0x20 316f9703d0SDarrick J. Wong #define ADT7470_REG_TEMP_BASE_ADDR 0x20 326f9703d0SDarrick J. Wong #define ADT7470_REG_TEMP_MAX_ADDR 0x29 336f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_BASE_ADDR 0x2A 346f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MAX_ADDR 0x31 356f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_BASE_ADDR 0x32 366f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MAX_ADDR 0x35 376f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MAX_BASE_ADDR 0x38 386f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MAX_MAX_ADDR 0x3B 396f9703d0SDarrick J. Wong #define ADT7470_REG_CFG 0x40 40ef67959cSChris Packham #define ADT7470_STRT_MASK 0x01 41ef67959cSChris Packham #define ADT7470_TEST_MASK 0x02 426f9703d0SDarrick J. Wong #define ADT7470_FSPD_MASK 0x04 43ef67959cSChris Packham #define ADT7470_T05_STB_MASK 0x80 446f9703d0SDarrick J. Wong #define ADT7470_REG_ALARM1 0x41 45fe03f28cSDarrick J. Wong #define ADT7470_R1T_ALARM 0x01 46fe03f28cSDarrick J. Wong #define ADT7470_R2T_ALARM 0x02 47fe03f28cSDarrick J. Wong #define ADT7470_R3T_ALARM 0x04 48fe03f28cSDarrick J. Wong #define ADT7470_R4T_ALARM 0x08 49fe03f28cSDarrick J. Wong #define ADT7470_R5T_ALARM 0x10 50fe03f28cSDarrick J. Wong #define ADT7470_R6T_ALARM 0x20 51fe03f28cSDarrick J. Wong #define ADT7470_R7T_ALARM 0x40 52fe03f28cSDarrick J. Wong #define ADT7470_OOL_ALARM 0x80 536f9703d0SDarrick J. Wong #define ADT7470_REG_ALARM2 0x42 54fe03f28cSDarrick J. Wong #define ADT7470_R8T_ALARM 0x01 55fe03f28cSDarrick J. Wong #define ADT7470_R9T_ALARM 0x02 56fe03f28cSDarrick J. Wong #define ADT7470_R10T_ALARM 0x04 57fe03f28cSDarrick J. Wong #define ADT7470_FAN1_ALARM 0x10 58fe03f28cSDarrick J. Wong #define ADT7470_FAN2_ALARM 0x20 59fe03f28cSDarrick J. Wong #define ADT7470_FAN3_ALARM 0x40 60fe03f28cSDarrick J. Wong #define ADT7470_FAN4_ALARM 0x80 616f9703d0SDarrick J. Wong #define ADT7470_REG_TEMP_LIMITS_BASE_ADDR 0x44 626f9703d0SDarrick J. Wong #define ADT7470_REG_TEMP_LIMITS_MAX_ADDR 0x57 636f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MIN_BASE_ADDR 0x58 646f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MIN_MAX_ADDR 0x5F 656f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MAX_BASE_ADDR 0x60 666f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MAX_MAX_ADDR 0x67 676f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_CFG_BASE_ADDR 0x68 686f9703d0SDarrick J. Wong #define ADT7470_REG_PWM12_CFG 0x68 696f9703d0SDarrick J. Wong #define ADT7470_PWM2_AUTO_MASK 0x40 706f9703d0SDarrick J. Wong #define ADT7470_PWM1_AUTO_MASK 0x80 712e75a4b7SDarrick J. Wong #define ADT7470_PWM_AUTO_MASK 0xC0 726f9703d0SDarrick J. Wong #define ADT7470_REG_PWM34_CFG 0x69 736f9703d0SDarrick J. Wong #define ADT7470_PWM3_AUTO_MASK 0x40 746f9703d0SDarrick J. Wong #define ADT7470_PWM4_AUTO_MASK 0x80 756f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A 766f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D 776f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E 786f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71 79aa18cc91SJoshua Scott #define ADT7470_REG_CFG_2 0x74 806f9703d0SDarrick J. Wong #define ADT7470_REG_ACOUSTICS12 0x75 816f9703d0SDarrick J. Wong #define ADT7470_REG_ACOUSTICS34 0x76 826f9703d0SDarrick J. Wong #define ADT7470_REG_DEVICE 0x3D 836f9703d0SDarrick J. Wong #define ADT7470_REG_VENDOR 0x3E 846f9703d0SDarrick J. Wong #define ADT7470_REG_REVISION 0x3F 856f9703d0SDarrick J. Wong #define ADT7470_REG_ALARM1_MASK 0x72 866f9703d0SDarrick J. Wong #define ADT7470_REG_ALARM2_MASK 0x73 876f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_AUTO_TEMP_BASE_ADDR 0x7C 886f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_AUTO_TEMP_MAX_ADDR 0x7D 896f9703d0SDarrick J. Wong #define ADT7470_REG_MAX_ADDR 0x81 906f9703d0SDarrick J. Wong 916f9703d0SDarrick J. Wong #define ADT7470_TEMP_COUNT 10 926f9703d0SDarrick J. Wong #define ADT7470_TEMP_REG(x) (ADT7470_REG_TEMP_BASE_ADDR + (x)) 936f9703d0SDarrick J. Wong #define ADT7470_TEMP_MIN_REG(x) (ADT7470_REG_TEMP_LIMITS_BASE_ADDR + ((x) * 2)) 946f9703d0SDarrick J. Wong #define ADT7470_TEMP_MAX_REG(x) (ADT7470_REG_TEMP_LIMITS_BASE_ADDR + \ 956f9703d0SDarrick J. Wong ((x) * 2) + 1) 966f9703d0SDarrick J. Wong 976f9703d0SDarrick J. Wong #define ADT7470_FAN_COUNT 4 986f9703d0SDarrick J. Wong #define ADT7470_REG_FAN(x) (ADT7470_REG_FAN_BASE_ADDR + ((x) * 2)) 996f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MIN(x) (ADT7470_REG_FAN_MIN_BASE_ADDR + ((x) * 2)) 1006f9703d0SDarrick J. Wong #define ADT7470_REG_FAN_MAX(x) (ADT7470_REG_FAN_MAX_BASE_ADDR + ((x) * 2)) 1016f9703d0SDarrick J. Wong 1026f9703d0SDarrick J. Wong #define ADT7470_PWM_COUNT 4 1036f9703d0SDarrick J. Wong #define ADT7470_REG_PWM(x) (ADT7470_REG_PWM_BASE_ADDR + (x)) 1046f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MAX(x) (ADT7470_REG_PWM_MAX_BASE_ADDR + (x)) 1056f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_MIN(x) (ADT7470_REG_PWM_MIN_BASE_ADDR + (x)) 1066f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_TMIN(x) (ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR + (x)) 1076f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_CFG(x) (ADT7470_REG_PWM_CFG_BASE_ADDR + ((x) / 2)) 1086f9703d0SDarrick J. Wong #define ADT7470_REG_PWM_AUTO_TEMP(x) (ADT7470_REG_PWM_AUTO_TEMP_BASE_ADDR + \ 1096f9703d0SDarrick J. Wong ((x) / 2)) 1106f9703d0SDarrick J. Wong 111fe03f28cSDarrick J. Wong #define ALARM2(x) ((x) << 8) 112fe03f28cSDarrick J. Wong 1136f9703d0SDarrick J. Wong #define ADT7470_VENDOR 0x41 1146f9703d0SDarrick J. Wong #define ADT7470_DEVICE 0x70 1156f9703d0SDarrick J. Wong /* datasheet only mentions a revision 2 */ 1166f9703d0SDarrick J. Wong #define ADT7470_REVISION 0x02 1176f9703d0SDarrick J. Wong 1186f9703d0SDarrick J. Wong /* "all temps" according to hwmon sysfs interface spec */ 1196f9703d0SDarrick J. Wong #define ADT7470_PWM_ALL_TEMPS 0x3FF 1206f9703d0SDarrick J. Wong 1216f9703d0SDarrick J. Wong /* How often do we reread sensors values? (In jiffies) */ 1226f9703d0SDarrick J. Wong #define SENSOR_REFRESH_INTERVAL (5 * HZ) 1236f9703d0SDarrick J. Wong 1246f9703d0SDarrick J. Wong /* How often do we reread sensor limit values? (In jiffies) */ 1256f9703d0SDarrick J. Wong #define LIMIT_REFRESH_INTERVAL (60 * HZ) 1266f9703d0SDarrick J. Wong 1272f22d5dfSDarrick J. Wong /* Wait at least 200ms per sensor for 10 sensors */ 1282f22d5dfSDarrick J. Wong #define TEMP_COLLECTION_TIME 2000 1296f9703d0SDarrick J. Wong 13089fac11cSDarrick J. Wong /* auto update thing won't fire more than every 2s */ 13189fac11cSDarrick J. Wong #define AUTO_UPDATE_INTERVAL 2000 13289fac11cSDarrick J. Wong 1336f9703d0SDarrick J. Wong /* datasheet says to divide this number by the fan reading to get fan rpm */ 1346f9703d0SDarrick J. Wong #define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) 1356f9703d0SDarrick J. Wong #define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM 1366f9703d0SDarrick J. Wong #define FAN_PERIOD_INVALID 65535 1376f9703d0SDarrick J. Wong #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) 1386f9703d0SDarrick J. Wong 139aa18cc91SJoshua Scott /* Config registers 1 and 2 include fields for selecting the PWM frequency */ 140aa18cc91SJoshua Scott #define ADT7470_CFG_LF 0x40 141aa18cc91SJoshua Scott #define ADT7470_FREQ_MASK 0x70 142aa18cc91SJoshua Scott #define ADT7470_FREQ_SHIFT 4 143aa18cc91SJoshua Scott 1446f9703d0SDarrick J. Wong struct adt7470_data { 145ef67959cSChris Packham struct regmap *regmap; 1466f9703d0SDarrick J. Wong struct mutex lock; 1476f9703d0SDarrick J. Wong char sensors_valid; 1486f9703d0SDarrick J. Wong char limits_valid; 1496f9703d0SDarrick J. Wong unsigned long sensors_last_updated; /* In jiffies */ 1506f9703d0SDarrick J. Wong unsigned long limits_last_updated; /* In jiffies */ 1516f9703d0SDarrick J. Wong 1522f22d5dfSDarrick J. Wong int num_temp_sensors; /* -1 = probe */ 15389fac11cSDarrick J. Wong int temperatures_probed; 1542f22d5dfSDarrick J. Wong 1556f9703d0SDarrick J. Wong s8 temp[ADT7470_TEMP_COUNT]; 1566f9703d0SDarrick J. Wong s8 temp_min[ADT7470_TEMP_COUNT]; 1576f9703d0SDarrick J. Wong s8 temp_max[ADT7470_TEMP_COUNT]; 1586f9703d0SDarrick J. Wong u16 fan[ADT7470_FAN_COUNT]; 1596f9703d0SDarrick J. Wong u16 fan_min[ADT7470_FAN_COUNT]; 1606f9703d0SDarrick J. Wong u16 fan_max[ADT7470_FAN_COUNT]; 161fe03f28cSDarrick J. Wong u16 alarm; 162fe03f28cSDarrick J. Wong u16 alarms_mask; 1636f9703d0SDarrick J. Wong u8 force_pwm_max; 1646f9703d0SDarrick J. Wong u8 pwm[ADT7470_PWM_COUNT]; 1656f9703d0SDarrick J. Wong u8 pwm_max[ADT7470_PWM_COUNT]; 1666f9703d0SDarrick J. Wong u8 pwm_automatic[ADT7470_PWM_COUNT]; 1676f9703d0SDarrick J. Wong u8 pwm_min[ADT7470_PWM_COUNT]; 1686f9703d0SDarrick J. Wong s8 pwm_tmin[ADT7470_PWM_COUNT]; 1696f9703d0SDarrick J. Wong u8 pwm_auto_temp[ADT7470_PWM_COUNT]; 17089fac11cSDarrick J. Wong 17189fac11cSDarrick J. Wong struct task_struct *auto_update; 17289fac11cSDarrick J. Wong unsigned int auto_update_interval; 1736f9703d0SDarrick J. Wong }; 1746f9703d0SDarrick J. Wong 1756f9703d0SDarrick J. Wong /* 1766f9703d0SDarrick J. Wong * 16-bit registers on the ADT7470 are low-byte first. The data sheet says 1776f9703d0SDarrick J. Wong * that the low byte must be read before the high byte. 1786f9703d0SDarrick J. Wong */ 179ef67959cSChris Packham static inline int adt7470_read_word_data(struct adt7470_data *data, unsigned int reg, 180ef67959cSChris Packham unsigned int *val) 1816f9703d0SDarrick J. Wong { 182ef67959cSChris Packham u8 regval[2]; 183ef67959cSChris Packham int err; 18423bd022aSChris Packham 185ef67959cSChris Packham err = regmap_bulk_read(data->regmap, reg, ®val, 2); 186ef67959cSChris Packham if (err < 0) 187ef67959cSChris Packham return err; 188ef67959cSChris Packham 189ef67959cSChris Packham *val = regval[0] | (regval[1] << 8); 190ef67959cSChris Packham 191ef67959cSChris Packham return 0; 1926f9703d0SDarrick J. Wong } 1936f9703d0SDarrick J. Wong 194ef67959cSChris Packham static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned int reg, 195ef67959cSChris Packham unsigned int val) 1966f9703d0SDarrick J. Wong { 197ef67959cSChris Packham u8 regval[2]; 198ef67959cSChris Packham 199ef67959cSChris Packham regval[0] = val & 0xFF; 200ef67959cSChris Packham regval[1] = val >> 8; 201ef67959cSChris Packham 202ef67959cSChris Packham return regmap_bulk_write(data->regmap, reg, ®val, 2); 2036f9703d0SDarrick J. Wong } 2046f9703d0SDarrick J. Wong 20589fac11cSDarrick J. Wong /* Probe for temperature sensors. Assumes lock is held */ 206ef67959cSChris Packham static int adt7470_read_temperatures(struct adt7470_data *data) 2076f9703d0SDarrick J. Wong { 20889fac11cSDarrick J. Wong unsigned long res; 209ef67959cSChris Packham unsigned int pwm_cfg[2]; 210ef67959cSChris Packham int err; 2116f9703d0SDarrick J. Wong int i; 212ef67959cSChris Packham u8 pwm[ADT7470_FAN_COUNT]; 2136f9703d0SDarrick J. Wong 2142e75a4b7SDarrick J. Wong /* save pwm[1-4] config register */ 215ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); 216ef67959cSChris Packham if (err < 0) 217ef67959cSChris Packham return err; 218ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(2), &pwm_cfg[1]); 219ef67959cSChris Packham if (err < 0) 220ef67959cSChris Packham return err; 2212e75a4b7SDarrick J. Wong 2222e75a4b7SDarrick J. Wong /* set manual pwm to whatever it is set to now */ 223ef67959cSChris Packham err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &pwm[0], 224ef67959cSChris Packham ADT7470_PWM_COUNT); 225ef67959cSChris Packham if (err < 0) 226ef67959cSChris Packham return err; 2272e75a4b7SDarrick J. Wong 2282e75a4b7SDarrick J. Wong /* put pwm in manual mode */ 229ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(0), 230ef67959cSChris Packham ADT7470_PWM_AUTO_MASK, 0); 231ef67959cSChris Packham if (err < 0) 232ef67959cSChris Packham return err; 233ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), 234ef67959cSChris Packham ADT7470_PWM_AUTO_MASK, 0); 235ef67959cSChris Packham if (err < 0) 236ef67959cSChris Packham return err; 2372e75a4b7SDarrick J. Wong 2382e75a4b7SDarrick J. Wong /* write pwm control to whatever it was */ 239ef67959cSChris Packham err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], 240ef67959cSChris Packham ADT7470_PWM_COUNT); 241ef67959cSChris Packham if (err < 0) 242ef67959cSChris Packham return err; 2432e75a4b7SDarrick J. Wong 2446f9703d0SDarrick J. Wong /* start reading temperature sensors */ 245ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, 246ef67959cSChris Packham ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); 247ef67959cSChris Packham if (err < 0) 248ef67959cSChris Packham return err; 2496f9703d0SDarrick J. Wong 2502f22d5dfSDarrick J. Wong /* Delay is 200ms * number of temp sensors. */ 25189fac11cSDarrick J. Wong res = msleep_interruptible((data->num_temp_sensors >= 0 ? 2522f22d5dfSDarrick J. Wong data->num_temp_sensors * 200 : 2532f22d5dfSDarrick J. Wong TEMP_COLLECTION_TIME)); 2546f9703d0SDarrick J. Wong 2556f9703d0SDarrick J. Wong /* done reading temperature sensors */ 256ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, 257ef67959cSChris Packham ADT7470_T05_STB_MASK, 0); 258ef67959cSChris Packham if (err < 0) 259ef67959cSChris Packham return err; 2606f9703d0SDarrick J. Wong 2612e75a4b7SDarrick J. Wong /* restore pwm[1-4] config registers */ 262ef67959cSChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); 263ef67959cSChris Packham if (err < 0) 264ef67959cSChris Packham return err; 265ef67959cSChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); 266ef67959cSChris Packham if (err < 0) 267ef67959cSChris Packham return err; 2682e75a4b7SDarrick J. Wong 269ef67959cSChris Packham if (res) 27089fac11cSDarrick J. Wong return -EAGAIN; 27189fac11cSDarrick J. Wong 27289fac11cSDarrick J. Wong /* Only count fans if we have to */ 27389fac11cSDarrick J. Wong if (data->num_temp_sensors >= 0) 27489fac11cSDarrick J. Wong return 0; 27589fac11cSDarrick J. Wong 276ef67959cSChris Packham err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0], 277ef67959cSChris Packham ADT7470_TEMP_COUNT); 278ef67959cSChris Packham if (err < 0) 279ef67959cSChris Packham return err; 28089fac11cSDarrick J. Wong for (i = 0; i < ADT7470_TEMP_COUNT; i++) { 28189fac11cSDarrick J. Wong if (data->temp[i]) 28289fac11cSDarrick J. Wong data->num_temp_sensors = i + 1; 28389fac11cSDarrick J. Wong } 28489fac11cSDarrick J. Wong data->temperatures_probed = 1; 28589fac11cSDarrick J. Wong return 0; 28689fac11cSDarrick J. Wong } 28789fac11cSDarrick J. Wong 28889fac11cSDarrick J. Wong static int adt7470_update_thread(void *p) 28989fac11cSDarrick J. Wong { 29089fac11cSDarrick J. Wong struct i2c_client *client = p; 29189fac11cSDarrick J. Wong struct adt7470_data *data = i2c_get_clientdata(client); 29289fac11cSDarrick J. Wong 29389fac11cSDarrick J. Wong while (!kthread_should_stop()) { 29489fac11cSDarrick J. Wong mutex_lock(&data->lock); 295ef67959cSChris Packham adt7470_read_temperatures(data); 29689fac11cSDarrick J. Wong mutex_unlock(&data->lock); 29793cacfd4SJoshua Scott 29889fac11cSDarrick J. Wong if (kthread_should_stop()) 29989fac11cSDarrick J. Wong break; 30093cacfd4SJoshua Scott 3017b2666ceSArmin Wolf schedule_timeout_interruptible(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 536fc958a61SChris 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 543fc958a61SChris Packham switch (attr) { 544fc958a61SChris Packham case hwmon_temp_input: 545fc958a61SChris Packham *val = 1000 * data->temp[channel]; 546fc958a61SChris Packham break; 547fc958a61SChris Packham case hwmon_temp_min: 548fc958a61SChris Packham *val = 1000 * data->temp_min[channel]; 549fc958a61SChris Packham break; 550fc958a61SChris Packham case hwmon_temp_max: 551fc958a61SChris Packham *val = 1000 * data->temp_max[channel]; 552fc958a61SChris Packham break; 553fc958a61SChris Packham case hwmon_temp_alarm: 554fc958a61SChris Packham *val = !!(data->alarm & channel); 555fc958a61SChris Packham break; 556fc958a61SChris Packham default: 557fc958a61SChris Packham return -EOPNOTSUPP; 5586f9703d0SDarrick J. Wong } 5596f9703d0SDarrick J. Wong 560fc958a61SChris Packham return 0; 561fc958a61SChris Packham } 562fc958a61SChris Packham 563fc958a61SChris 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 568fc958a61SChris Packham val = clamp_val(val, -128000, 127000); 569fc958a61SChris Packham val = DIV_ROUND_CLOSEST(val, 1000); 57005a9bd46SDarrick J. Wong 571fc958a61SChris Packham switch (attr) { 572fc958a61SChris Packham case hwmon_temp_min: 5736f9703d0SDarrick J. Wong mutex_lock(&data->lock); 574fc958a61SChris Packham data->temp_min[channel] = val; 575fc958a61SChris Packham err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); 5766f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 577fc958a61SChris Packham break; 578fc958a61SChris Packham case hwmon_temp_max: 5796f9703d0SDarrick J. Wong mutex_lock(&data->lock); 580fc958a61SChris Packham data->temp_max[channel] = val; 581fc958a61SChris Packham err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); 5826f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 583fc958a61SChris Packham break; 584fc958a61SChris Packham default: 585fc958a61SChris Packham return -EOPNOTSUPP; 5866f9703d0SDarrick J. Wong } 5876f9703d0SDarrick J. Wong 588fc958a61SChris Packham return err; 5896f9703d0SDarrick J. Wong } 5906f9703d0SDarrick J. Wong 591808fc6c2SJulia Lawall static ssize_t alarm_mask_show(struct device *dev, 592fc958a61SChris 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 624fc958a61SChris 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 631fc958a61SChris Packham switch (attr) { 632fc958a61SChris Packham case hwmon_fan_input: 633fc958a61SChris Packham if (FAN_DATA_VALID(data->fan[channel])) 634fc958a61SChris Packham *val = FAN_PERIOD_TO_RPM(data->fan[channel]); 6356f9703d0SDarrick J. Wong else 636fc958a61SChris Packham *val = 0; 637fc958a61SChris Packham break; 638fc958a61SChris Packham case hwmon_fan_min: 639fc958a61SChris Packham if (FAN_DATA_VALID(data->fan_min[channel])) 640fc958a61SChris Packham *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); 641fc958a61SChris Packham else 642fc958a61SChris Packham *val = 0; 643fc958a61SChris Packham break; 644fc958a61SChris Packham case hwmon_fan_max: 645fc958a61SChris Packham if (FAN_DATA_VALID(data->fan_max[channel])) 646fc958a61SChris Packham *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); 647fc958a61SChris Packham else 648fc958a61SChris Packham *val = 0; 649fc958a61SChris Packham break; 650fc958a61SChris Packham case hwmon_fan_alarm: 651fc958a61SChris Packham *val = !!(data->alarm & (1 << (12 + channel))); 652fc958a61SChris Packham break; 653fc958a61SChris Packham default: 654fc958a61SChris Packham return -EOPNOTSUPP; 6556f9703d0SDarrick J. Wong } 6566f9703d0SDarrick J. Wong 657fc958a61SChris Packham return 0; 658fc958a61SChris Packham } 659fc958a61SChris Packham 660fc958a61SChris 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 665c1ec0cabSDan Carpenter if (val <= 0) 666c1ec0cabSDan Carpenter return -EINVAL; 667c1ec0cabSDan Carpenter 668fc958a61SChris Packham val = FAN_RPM_TO_PERIOD(val); 669fc958a61SChris Packham val = clamp_val(val, 1, 65534); 67005a9bd46SDarrick J. Wong 671fc958a61SChris Packham switch (attr) { 672fc958a61SChris Packham case hwmon_fan_min: 6736f9703d0SDarrick J. Wong mutex_lock(&data->lock); 674fc958a61SChris Packham data->fan_min[channel] = val; 675fc958a61SChris Packham err = adt7470_write_word_data(data, ADT7470_REG_FAN_MIN(channel), val); 6766f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 677fc958a61SChris Packham break; 678fc958a61SChris Packham case hwmon_fan_max: 6796f9703d0SDarrick J. Wong mutex_lock(&data->lock); 680fc958a61SChris Packham data->fan_max[channel] = val; 681fc958a61SChris Packham err = adt7470_write_word_data(data, ADT7470_REG_FAN_MAX(channel), val); 6826f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 683fc958a61SChris Packham break; 684fc958a61SChris Packham default: 685fc958a61SChris Packham return -EOPNOTSUPP; 6866f9703d0SDarrick J. Wong } 6876f9703d0SDarrick J. Wong 688fc958a61SChris Packham return err; 6896f9703d0SDarrick J. Wong } 6906f9703d0SDarrick J. Wong 69142291a5aSGuenter Roeck static ssize_t force_pwm_max_show(struct device *dev, 69242291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 6936f9703d0SDarrick J. Wong { 6946f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 695ad00a02eSChris Packham 696ad00a02eSChris Packham if (IS_ERR(data)) 697ad00a02eSChris Packham return PTR_ERR(data); 698ad00a02eSChris Packham 6996f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", data->force_pwm_max); 7006f9703d0SDarrick J. Wong } 7016f9703d0SDarrick J. Wong 70242291a5aSGuenter Roeck static ssize_t force_pwm_max_store(struct device *dev, 7036f9703d0SDarrick J. Wong struct device_attribute *devattr, 70442291a5aSGuenter Roeck const char *buf, size_t count) 7056f9703d0SDarrick J. Wong { 70630485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 70705a9bd46SDarrick J. Wong long temp; 708ef67959cSChris Packham int err; 7096f9703d0SDarrick J. Wong 710179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 71105a9bd46SDarrick J. Wong return -EINVAL; 71205a9bd46SDarrick J. Wong 7136f9703d0SDarrick J. Wong mutex_lock(&data->lock); 7146f9703d0SDarrick J. Wong data->force_pwm_max = temp; 715ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, 716ef67959cSChris Packham ADT7470_FSPD_MASK, 717ef67959cSChris Packham temp ? ADT7470_FSPD_MASK : 0); 7186f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 7196f9703d0SDarrick J. Wong 720ef67959cSChris Packham return err < 0 ? err : count; 7216f9703d0SDarrick J. Wong } 7226f9703d0SDarrick J. Wong 723aa18cc91SJoshua Scott /* These are the valid PWM frequencies to the nearest Hz */ 724aa18cc91SJoshua Scott static const int adt7470_freq_map[] = { 725aa18cc91SJoshua Scott 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 726aa18cc91SJoshua Scott }; 727aa18cc91SJoshua Scott 728fc958a61SChris Packham static int pwm1_freq_get(struct device *dev) 729aa18cc91SJoshua Scott { 730fc958a61SChris Packham struct adt7470_data *data = dev_get_drvdata(dev); 731ef67959cSChris Packham unsigned int cfg_reg_1, cfg_reg_2; 732aa18cc91SJoshua Scott int index; 733ef67959cSChris Packham int err; 734ef67959cSChris Packham 735aa18cc91SJoshua Scott mutex_lock(&data->lock); 736ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1); 737ef67959cSChris Packham if (err < 0) 738ef67959cSChris Packham goto out; 739ef67959cSChris Packham err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2); 740ef67959cSChris Packham if (err < 0) 741ef67959cSChris Packham goto out; 742aa18cc91SJoshua Scott mutex_unlock(&data->lock); 743aa18cc91SJoshua Scott 744aa18cc91SJoshua Scott index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; 745aa18cc91SJoshua Scott if (!(cfg_reg_1 & ADT7470_CFG_LF)) 746aa18cc91SJoshua Scott index += 8; 747aa18cc91SJoshua Scott if (index >= ARRAY_SIZE(adt7470_freq_map)) 748aa18cc91SJoshua Scott index = ARRAY_SIZE(adt7470_freq_map) - 1; 749aa18cc91SJoshua Scott 750fc958a61SChris Packham return adt7470_freq_map[index]; 751ef67959cSChris Packham 752ef67959cSChris Packham out: 753ef67959cSChris Packham mutex_unlock(&data->lock); 754ef67959cSChris Packham return err; 755aa18cc91SJoshua Scott } 756aa18cc91SJoshua Scott 757fc958a61SChris Packham static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val) 758fc958a61SChris Packham { 759fc958a61SChris Packham struct adt7470_data *data = adt7470_update_device(dev); 760fc958a61SChris Packham 761fc958a61SChris Packham if (IS_ERR(data)) 762fc958a61SChris Packham return PTR_ERR(data); 763fc958a61SChris Packham 764fc958a61SChris Packham switch (attr) { 765fc958a61SChris Packham case hwmon_pwm_input: 766fc958a61SChris Packham *val = data->pwm[channel]; 767fc958a61SChris Packham break; 768fc958a61SChris Packham case hwmon_pwm_enable: 769fc958a61SChris Packham *val = 1 + data->pwm_automatic[channel]; 770fc958a61SChris Packham break; 771fc958a61SChris Packham case hwmon_pwm_freq: 772fc958a61SChris Packham *val = pwm1_freq_get(dev); 773fc958a61SChris Packham break; 774fc958a61SChris Packham default: 775fc958a61SChris Packham return -EOPNOTSUPP; 776fc958a61SChris Packham } 777fc958a61SChris Packham 778fc958a61SChris Packham return 0; 779fc958a61SChris Packham } 780fc958a61SChris Packham 781fc958a61SChris Packham static int pwm1_freq_set(struct device *dev, long freq) 782aa18cc91SJoshua Scott { 783aa18cc91SJoshua Scott struct adt7470_data *data = dev_get_drvdata(dev); 784fc958a61SChris Packham unsigned int low_freq = ADT7470_CFG_LF; 785aa18cc91SJoshua Scott int index; 786ef67959cSChris Packham int err; 787aa18cc91SJoshua Scott 788aa18cc91SJoshua Scott /* Round the user value given to the closest available frequency */ 789aa18cc91SJoshua Scott index = find_closest(freq, adt7470_freq_map, 790aa18cc91SJoshua Scott ARRAY_SIZE(adt7470_freq_map)); 791aa18cc91SJoshua Scott 792aa18cc91SJoshua Scott if (index >= 8) { 793aa18cc91SJoshua Scott index -= 8; 794aa18cc91SJoshua Scott low_freq = 0; 795aa18cc91SJoshua Scott } 796aa18cc91SJoshua Scott 797aa18cc91SJoshua Scott mutex_lock(&data->lock); 798aa18cc91SJoshua Scott /* Configuration Register 1 */ 799ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, 800ef67959cSChris Packham ADT7470_CFG_LF, low_freq); 801ef67959cSChris Packham if (err < 0) 802ef67959cSChris Packham goto out; 803ef67959cSChris Packham 804aa18cc91SJoshua Scott /* Configuration Register 2 */ 805ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, 806ef67959cSChris Packham ADT7470_FREQ_MASK, 807ef67959cSChris Packham index << ADT7470_FREQ_SHIFT); 808ef67959cSChris Packham out: 809aa18cc91SJoshua Scott mutex_unlock(&data->lock); 810aa18cc91SJoshua Scott 811fc958a61SChris Packham return err; 812fc958a61SChris Packham } 813fc958a61SChris Packham 814fc958a61SChris Packham static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val) 815fc958a61SChris Packham { 816fc958a61SChris Packham struct adt7470_data *data = dev_get_drvdata(dev); 817fc958a61SChris Packham unsigned int pwm_auto_reg_mask; 818fc958a61SChris Packham int err; 819fc958a61SChris Packham 820fc958a61SChris Packham switch (attr) { 821fc958a61SChris Packham case hwmon_pwm_input: 822fc958a61SChris Packham val = clamp_val(val, 0, 255); 823fc958a61SChris Packham mutex_lock(&data->lock); 824fc958a61SChris Packham data->pwm[channel] = val; 825fc958a61SChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), 826fc958a61SChris Packham data->pwm[channel]); 827fc958a61SChris Packham mutex_unlock(&data->lock); 828fc958a61SChris Packham break; 829fc958a61SChris Packham case hwmon_pwm_enable: 830fc958a61SChris Packham if (channel % 2) 831fc958a61SChris Packham pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; 832fc958a61SChris Packham else 833fc958a61SChris Packham pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK; 834fc958a61SChris Packham 835fc958a61SChris Packham if (val != 2 && val != 1) 836fc958a61SChris Packham return -EINVAL; 837fc958a61SChris Packham val--; 838fc958a61SChris Packham 839fc958a61SChris Packham mutex_lock(&data->lock); 840fc958a61SChris Packham data->pwm_automatic[channel] = val; 841fc958a61SChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), 842fc958a61SChris Packham pwm_auto_reg_mask, 843fc958a61SChris Packham val ? pwm_auto_reg_mask : 0); 844fc958a61SChris Packham mutex_unlock(&data->lock); 845fc958a61SChris Packham break; 846fc958a61SChris Packham case hwmon_pwm_freq: 847fc958a61SChris Packham err = pwm1_freq_set(dev, val); 848fc958a61SChris Packham break; 849fc958a61SChris Packham default: 850fc958a61SChris Packham return -EOPNOTSUPP; 851fc958a61SChris Packham } 852fc958a61SChris Packham 853fc958a61SChris Packham return err; 854aa18cc91SJoshua Scott } 855aa18cc91SJoshua Scott 85642291a5aSGuenter Roeck static ssize_t pwm_max_show(struct device *dev, 85742291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 8586f9703d0SDarrick J. Wong { 8596f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 8606f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 861ad00a02eSChris Packham 862ad00a02eSChris Packham if (IS_ERR(data)) 863ad00a02eSChris Packham return PTR_ERR(data); 864ad00a02eSChris Packham 8656f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", data->pwm_max[attr->index]); 8666f9703d0SDarrick J. Wong } 8676f9703d0SDarrick J. Wong 86842291a5aSGuenter Roeck static ssize_t pwm_max_store(struct device *dev, 8696f9703d0SDarrick J. Wong struct device_attribute *devattr, 87042291a5aSGuenter Roeck const char *buf, size_t count) 8716f9703d0SDarrick J. Wong { 8726f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 87330485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 87405a9bd46SDarrick J. Wong long temp; 875ef67959cSChris Packham int err; 87605a9bd46SDarrick J. Wong 877179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 87805a9bd46SDarrick J. Wong return -EINVAL; 87905a9bd46SDarrick J. Wong 8802a844c14SGuenter Roeck temp = clamp_val(temp, 0, 255); 8816f9703d0SDarrick J. Wong 8826f9703d0SDarrick J. Wong mutex_lock(&data->lock); 8836f9703d0SDarrick J. Wong data->pwm_max[attr->index] = temp; 884ef67959cSChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM_MAX(attr->index), 8856f9703d0SDarrick J. Wong temp); 8866f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 8876f9703d0SDarrick J. Wong 888ef67959cSChris Packham return err < 0 ? err : count; 8896f9703d0SDarrick J. Wong } 8906f9703d0SDarrick J. Wong 89142291a5aSGuenter Roeck static ssize_t pwm_min_show(struct device *dev, 89242291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 8936f9703d0SDarrick J. Wong { 8946f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 8956f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 896ad00a02eSChris Packham 897ad00a02eSChris Packham if (IS_ERR(data)) 898ad00a02eSChris Packham return PTR_ERR(data); 899ad00a02eSChris Packham 9006f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", data->pwm_min[attr->index]); 9016f9703d0SDarrick J. Wong } 9026f9703d0SDarrick J. Wong 90342291a5aSGuenter Roeck static ssize_t pwm_min_store(struct device *dev, 9046f9703d0SDarrick J. Wong struct device_attribute *devattr, 90542291a5aSGuenter Roeck const char *buf, size_t count) 9066f9703d0SDarrick J. Wong { 9076f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 90830485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 90905a9bd46SDarrick J. Wong long temp; 910ef67959cSChris Packham int err; 91105a9bd46SDarrick J. Wong 912179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 91305a9bd46SDarrick J. Wong return -EINVAL; 91405a9bd46SDarrick J. Wong 9152a844c14SGuenter Roeck temp = clamp_val(temp, 0, 255); 9166f9703d0SDarrick J. Wong 9176f9703d0SDarrick J. Wong mutex_lock(&data->lock); 9186f9703d0SDarrick J. Wong data->pwm_min[attr->index] = temp; 919ef67959cSChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM_MIN(attr->index), 9206f9703d0SDarrick J. Wong temp); 9216f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 9226f9703d0SDarrick J. Wong 923ef67959cSChris Packham return err < 0 ? err : count; 9246f9703d0SDarrick J. Wong } 9256f9703d0SDarrick J. Wong 92642291a5aSGuenter Roeck static ssize_t pwm_tmax_show(struct device *dev, 92742291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 9286f9703d0SDarrick J. Wong { 9296f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 9306f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 931ad00a02eSChris Packham 932ad00a02eSChris Packham if (IS_ERR(data)) 933ad00a02eSChris Packham return PTR_ERR(data); 934ad00a02eSChris Packham 9356f9703d0SDarrick J. Wong /* the datasheet says that tmax = tmin + 20C */ 9366f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", 1000 * (20 + data->pwm_tmin[attr->index])); 9376f9703d0SDarrick J. Wong } 9386f9703d0SDarrick J. Wong 93942291a5aSGuenter Roeck static ssize_t pwm_tmin_show(struct device *dev, 94042291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 9416f9703d0SDarrick J. Wong { 9426f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 9436f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 944ad00a02eSChris Packham 945ad00a02eSChris Packham if (IS_ERR(data)) 946ad00a02eSChris Packham return PTR_ERR(data); 947ad00a02eSChris Packham 9486f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", 1000 * data->pwm_tmin[attr->index]); 9496f9703d0SDarrick J. Wong } 9506f9703d0SDarrick J. Wong 95142291a5aSGuenter Roeck static ssize_t pwm_tmin_store(struct device *dev, 9526f9703d0SDarrick J. Wong struct device_attribute *devattr, 95342291a5aSGuenter Roeck const char *buf, size_t count) 9546f9703d0SDarrick J. Wong { 9556f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 95630485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 95705a9bd46SDarrick J. Wong long temp; 958ef67959cSChris Packham int err; 95905a9bd46SDarrick J. Wong 960179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 96105a9bd46SDarrick J. Wong return -EINVAL; 96205a9bd46SDarrick J. Wong 96364bd708aSGuenter Roeck temp = clamp_val(temp, -128000, 127000); 9648f8c1fb0SDarrick J. Wong temp = DIV_ROUND_CLOSEST(temp, 1000); 9656f9703d0SDarrick J. Wong 9666f9703d0SDarrick J. Wong mutex_lock(&data->lock); 9676f9703d0SDarrick J. Wong data->pwm_tmin[attr->index] = temp; 968ef67959cSChris Packham err = regmap_write(data->regmap, ADT7470_REG_PWM_TMIN(attr->index), 9696f9703d0SDarrick J. Wong temp); 9706f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 9716f9703d0SDarrick J. Wong 972ef67959cSChris Packham return err < 0 ? err : count; 9736f9703d0SDarrick J. Wong } 9746f9703d0SDarrick J. Wong 97542291a5aSGuenter Roeck static ssize_t pwm_auto_temp_show(struct device *dev, 97642291a5aSGuenter Roeck struct device_attribute *devattr, char *buf) 9776f9703d0SDarrick J. Wong { 9786f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 9796f9703d0SDarrick J. Wong struct adt7470_data *data = adt7470_update_device(dev); 980ad00a02eSChris Packham u8 ctrl; 9816f9703d0SDarrick J. Wong 982ad00a02eSChris Packham if (IS_ERR(data)) 983ad00a02eSChris Packham return PTR_ERR(data); 984ad00a02eSChris Packham 985ad00a02eSChris Packham ctrl = data->pwm_auto_temp[attr->index]; 9866f9703d0SDarrick J. Wong if (ctrl) 9876f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", 1 << (ctrl - 1)); 9886f9703d0SDarrick J. Wong else 9896f9703d0SDarrick J. Wong return sprintf(buf, "%d\n", ADT7470_PWM_ALL_TEMPS); 9906f9703d0SDarrick J. Wong } 9916f9703d0SDarrick J. Wong 9926f9703d0SDarrick J. Wong static int cvt_auto_temp(int input) 9936f9703d0SDarrick J. Wong { 9946f9703d0SDarrick J. Wong if (input == ADT7470_PWM_ALL_TEMPS) 9956f9703d0SDarrick J. Wong return 0; 996ce9c2f44SRobert P. J. Day if (input < 1 || !is_power_of_2(input)) 9976f9703d0SDarrick J. Wong return -EINVAL; 9986f9703d0SDarrick J. Wong return ilog2(input) + 1; 9996f9703d0SDarrick J. Wong } 10006f9703d0SDarrick J. Wong 100142291a5aSGuenter Roeck static ssize_t pwm_auto_temp_store(struct device *dev, 10026f9703d0SDarrick J. Wong struct device_attribute *devattr, 100342291a5aSGuenter Roeck const char *buf, size_t count) 10046f9703d0SDarrick J. Wong { 10056f9703d0SDarrick J. Wong struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 100630485776SAxel Lin struct adt7470_data *data = dev_get_drvdata(dev); 10076f9703d0SDarrick J. Wong int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index); 1008ef67959cSChris Packham unsigned int mask, val; 100905a9bd46SDarrick J. Wong long temp; 1010ef67959cSChris Packham int err; 10116f9703d0SDarrick J. Wong 1012179c4fdbSFrans Meulenbroeks if (kstrtol(buf, 10, &temp)) 101305a9bd46SDarrick J. Wong return -EINVAL; 101405a9bd46SDarrick J. Wong 101505a9bd46SDarrick J. Wong temp = cvt_auto_temp(temp); 10166f9703d0SDarrick J. Wong if (temp < 0) 10176f9703d0SDarrick J. Wong return temp; 10186f9703d0SDarrick J. Wong 10196f9703d0SDarrick J. Wong mutex_lock(&data->lock); 10206f9703d0SDarrick J. Wong data->pwm_automatic[attr->index] = temp; 10216f9703d0SDarrick J. Wong 10226f9703d0SDarrick J. Wong if (!(attr->index % 2)) { 1023ef67959cSChris Packham mask = 0xF0; 1024ef67959cSChris Packham val = (temp << 4) & 0xF0; 10256f9703d0SDarrick J. Wong } else { 1026ef67959cSChris Packham mask = 0x0F; 1027ef67959cSChris Packham val = temp & 0x0F; 10286f9703d0SDarrick J. Wong } 10296f9703d0SDarrick J. Wong 1030ef67959cSChris Packham err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); 10316f9703d0SDarrick J. Wong mutex_unlock(&data->lock); 10326f9703d0SDarrick J. Wong 1033ef67959cSChris Packham return err < 0 ? err : count; 10346f9703d0SDarrick J. Wong } 10356f9703d0SDarrick J. Wong 1036808fc6c2SJulia Lawall static DEVICE_ATTR_RW(alarm_mask); 1037808fc6c2SJulia Lawall static DEVICE_ATTR_RW(num_temp_sensors); 1038808fc6c2SJulia Lawall static DEVICE_ATTR_RW(auto_update_interval); 10396f9703d0SDarrick J. Wong 104042291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(force_pwm_max, force_pwm_max, 0); 10416f9703d0SDarrick J. Wong 104242291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_pwm, pwm_min, 0); 104342291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_pwm, pwm_min, 1); 104442291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point1_pwm, pwm_min, 2); 104542291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point1_pwm, pwm_min, 3); 10466f9703d0SDarrick J. Wong 104742291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_pwm, pwm_max, 0); 104842291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point2_pwm, pwm_max, 1); 104942291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point2_pwm, pwm_max, 2); 105042291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point2_pwm, pwm_max, 3); 10516f9703d0SDarrick J. Wong 105242291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, pwm_tmin, 0); 105342291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_temp, pwm_tmin, 1); 105442291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point1_temp, pwm_tmin, 2); 105542291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm4_auto_point1_temp, pwm_tmin, 3); 10566f9703d0SDarrick J. Wong 105742291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point2_temp, pwm_tmax, 0); 105842291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(pwm2_auto_point2_temp, pwm_tmax, 1); 105942291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(pwm3_auto_point2_temp, pwm_tmax, 2); 106042291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(pwm4_auto_point2_temp, pwm_tmax, 3); 10616f9703d0SDarrick J. Wong 106242291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm1_auto_channels_temp, pwm_auto_temp, 0); 106342291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm2_auto_channels_temp, pwm_auto_temp, 1); 106442291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm3_auto_channels_temp, pwm_auto_temp, 2); 106542291a5aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(pwm4_auto_channels_temp, pwm_auto_temp, 3); 10666f9703d0SDarrick J. Wong 106730485776SAxel Lin static struct attribute *adt7470_attrs[] = { 1068fe03f28cSDarrick J. Wong &dev_attr_alarm_mask.attr, 10692f22d5dfSDarrick J. Wong &dev_attr_num_temp_sensors.attr, 107089fac11cSDarrick J. Wong &dev_attr_auto_update_interval.attr, 10716f9703d0SDarrick J. Wong &sensor_dev_attr_force_pwm_max.dev_attr.attr, 10726f9703d0SDarrick J. Wong &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, 10736f9703d0SDarrick J. Wong &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, 10746f9703d0SDarrick J. Wong &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, 10756f9703d0SDarrick J. Wong &sensor_dev_attr_pwm4_auto_point1_pwm.dev_attr.attr, 10766f9703d0SDarrick J. Wong &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, 10776f9703d0SDarrick J. Wong &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, 10786f9703d0SDarrick J. Wong &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, 10796f9703d0SDarrick J. Wong &sensor_dev_attr_pwm4_auto_point2_pwm.dev_attr.attr, 10806f9703d0SDarrick J. Wong &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, 10816f9703d0SDarrick J. Wong &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, 10826f9703d0SDarrick J. Wong &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr, 10836f9703d0SDarrick J. Wong &sensor_dev_attr_pwm4_auto_point1_temp.dev_attr.attr, 10846f9703d0SDarrick J. Wong &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, 10856f9703d0SDarrick J. Wong &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, 10866f9703d0SDarrick J. Wong &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, 10876f9703d0SDarrick J. Wong &sensor_dev_attr_pwm4_auto_point2_temp.dev_attr.attr, 10886f9703d0SDarrick J. Wong &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, 10896f9703d0SDarrick J. Wong &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, 10906f9703d0SDarrick J. Wong &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, 10916f9703d0SDarrick J. Wong &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr, 10926f9703d0SDarrick J. Wong NULL 10936f9703d0SDarrick J. Wong }; 10946f9703d0SDarrick J. Wong 109530485776SAxel Lin ATTRIBUTE_GROUPS(adt7470); 109630485776SAxel Lin 1097fc958a61SChris Packham static int adt7470_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, 1098fc958a61SChris Packham int channel, long *val) 1099fc958a61SChris Packham { 1100fc958a61SChris Packham switch (type) { 1101fc958a61SChris Packham case hwmon_temp: 1102fc958a61SChris Packham return adt7470_temp_read(dev, attr, channel, val); 1103fc958a61SChris Packham case hwmon_fan: 1104fc958a61SChris Packham return adt7470_fan_read(dev, attr, channel, val); 1105fc958a61SChris Packham case hwmon_pwm: 1106fc958a61SChris Packham return adt7470_pwm_read(dev, attr, channel, val); 1107fc958a61SChris Packham default: 1108fc958a61SChris Packham return -EOPNOTSUPP; 1109fc958a61SChris Packham } 1110fc958a61SChris Packham } 1111fc958a61SChris Packham 1112fc958a61SChris Packham static int adt7470_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, 1113fc958a61SChris Packham int channel, long val) 1114fc958a61SChris Packham { 1115fc958a61SChris Packham switch (type) { 1116fc958a61SChris Packham case hwmon_temp: 1117fc958a61SChris Packham return adt7470_temp_write(dev, attr, channel, val); 1118fc958a61SChris Packham case hwmon_fan: 1119fc958a61SChris Packham return adt7470_fan_write(dev, attr, channel, val); 1120fc958a61SChris Packham case hwmon_pwm: 1121fc958a61SChris Packham return adt7470_pwm_write(dev, attr, channel, val); 1122fc958a61SChris Packham default: 1123fc958a61SChris Packham return -EOPNOTSUPP; 1124fc958a61SChris Packham } 1125fc958a61SChris Packham } 1126fc958a61SChris Packham 1127fc958a61SChris Packham static umode_t adt7470_is_visible(const void *_data, enum hwmon_sensor_types type, 1128fc958a61SChris Packham u32 attr, int channel) 1129fc958a61SChris Packham { 1130fc958a61SChris Packham umode_t mode = 0; 1131fc958a61SChris Packham 1132fc958a61SChris Packham switch (type) { 1133fc958a61SChris Packham case hwmon_temp: 1134fc958a61SChris Packham switch (attr) { 1135fc958a61SChris Packham case hwmon_temp: 1136fc958a61SChris Packham case hwmon_temp_alarm: 1137fc958a61SChris Packham mode = 0444; 1138fc958a61SChris Packham break; 1139fc958a61SChris Packham case hwmon_temp_min: 1140fc958a61SChris Packham case hwmon_temp_max: 1141fc958a61SChris Packham mode = 0644; 1142fc958a61SChris Packham break; 1143fc958a61SChris Packham default: 1144fc958a61SChris Packham break; 1145fc958a61SChris Packham } 1146fc958a61SChris Packham break; 1147fc958a61SChris Packham case hwmon_fan: 1148fc958a61SChris Packham switch (attr) { 1149fc958a61SChris Packham case hwmon_fan_input: 1150fc958a61SChris Packham case hwmon_fan_alarm: 1151fc958a61SChris Packham mode = 0444; 1152fc958a61SChris Packham break; 1153fc958a61SChris Packham case hwmon_fan_min: 1154fc958a61SChris Packham case hwmon_fan_max: 1155fc958a61SChris Packham mode = 0644; 1156fc958a61SChris Packham break; 1157fc958a61SChris Packham default: 1158fc958a61SChris Packham break; 1159fc958a61SChris Packham } 1160fc958a61SChris Packham break; 1161fc958a61SChris Packham case hwmon_pwm: 1162fc958a61SChris Packham switch (attr) { 1163fc958a61SChris Packham case hwmon_pwm_input: 1164fc958a61SChris Packham case hwmon_pwm_enable: 1165fc958a61SChris Packham mode = 0644; 1166fc958a61SChris Packham break; 1167fc958a61SChris Packham case hwmon_pwm_freq: 1168fc958a61SChris Packham if (channel == 0) 1169fc958a61SChris Packham mode = 0644; 1170fc958a61SChris Packham else 1171fc958a61SChris Packham mode = 0; 1172fc958a61SChris Packham break; 1173fc958a61SChris Packham default: 1174fc958a61SChris Packham break; 1175fc958a61SChris Packham } 1176fc958a61SChris Packham break; 1177fc958a61SChris Packham default: 1178fc958a61SChris Packham break; 1179fc958a61SChris Packham } 1180fc958a61SChris Packham 1181fc958a61SChris Packham return mode; 1182fc958a61SChris Packham } 1183fc958a61SChris Packham 1184fc958a61SChris Packham static const struct hwmon_ops adt7470_hwmon_ops = { 1185fc958a61SChris Packham .is_visible = adt7470_is_visible, 1186fc958a61SChris Packham .read = adt7470_read, 1187fc958a61SChris Packham .write = adt7470_write, 1188fc958a61SChris Packham }; 1189fc958a61SChris Packham 1190ce94ff2eSKrzysztof Kozlowski static const struct hwmon_channel_info * const adt7470_info[] = { 1191fc958a61SChris Packham HWMON_CHANNEL_INFO(temp, 1192fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1193fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1194fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1195fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1196fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1197fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1198fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1199fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1200fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, 1201fc958a61SChris Packham HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM), 1202fc958a61SChris Packham HWMON_CHANNEL_INFO(fan, 1203fc958a61SChris Packham HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, 1204fc958a61SChris Packham HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, 1205fc958a61SChris Packham HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, 1206fc958a61SChris Packham HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM), 1207fc958a61SChris Packham HWMON_CHANNEL_INFO(pwm, 1208fc958a61SChris Packham HWMON_PWM_INPUT | HWMON_PWM_ENABLE | HWMON_PWM_FREQ, 1209fc958a61SChris Packham HWMON_PWM_INPUT | HWMON_PWM_ENABLE, 1210fc958a61SChris Packham HWMON_PWM_INPUT | HWMON_PWM_ENABLE, 1211fc958a61SChris Packham HWMON_PWM_INPUT | HWMON_PWM_ENABLE), 1212fc958a61SChris Packham NULL 1213fc958a61SChris Packham }; 1214fc958a61SChris Packham 1215fc958a61SChris Packham static const struct hwmon_chip_info adt7470_chip_info = { 1216fc958a61SChris Packham .ops = &adt7470_hwmon_ops, 1217fc958a61SChris Packham .info = adt7470_info, 1218fc958a61SChris Packham }; 1219fc958a61SChris Packham 1220008f1ca5SJean Delvare /* Return 0 if detection is successful, -ENODEV otherwise */ 1221310ec792SJean Delvare static int adt7470_detect(struct i2c_client *client, 1222008f1ca5SJean Delvare struct i2c_board_info *info) 12236f9703d0SDarrick J. Wong { 1224008f1ca5SJean Delvare struct i2c_adapter *adapter = client->adapter; 122552df6440SJean Delvare int vendor, device, revision; 12266f9703d0SDarrick J. Wong 12276f9703d0SDarrick J. Wong if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 1228008f1ca5SJean Delvare return -ENODEV; 12296f9703d0SDarrick J. Wong 12306f9703d0SDarrick J. Wong vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR); 1231008f1ca5SJean Delvare if (vendor != ADT7470_VENDOR) 1232008f1ca5SJean Delvare return -ENODEV; 12336f9703d0SDarrick J. Wong 12346f9703d0SDarrick J. Wong device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE); 1235008f1ca5SJean Delvare if (device != ADT7470_DEVICE) 1236008f1ca5SJean Delvare return -ENODEV; 12376f9703d0SDarrick J. Wong 123852df6440SJean Delvare revision = i2c_smbus_read_byte_data(client, ADT7470_REG_REVISION); 1239008f1ca5SJean Delvare if (revision != ADT7470_REVISION) 1240008f1ca5SJean Delvare return -ENODEV; 12416f9703d0SDarrick J. Wong 124223bd022aSChris Packham strscpy(info->type, "adt7470", I2C_NAME_SIZE); 12436f9703d0SDarrick J. Wong 1244008f1ca5SJean Delvare return 0; 1245008f1ca5SJean Delvare } 1246008f1ca5SJean Delvare 1247ef67959cSChris Packham static const struct regmap_config adt7470_regmap_config = { 1248ef67959cSChris Packham .reg_bits = 8, 1249ef67959cSChris Packham .val_bits = 8, 1250ef67959cSChris Packham .use_single_read = true, 1251ef67959cSChris Packham .use_single_write = true, 1252ef67959cSChris Packham }; 12539027d933SAxel Lin 125467487038SStephen Kitt static int adt7470_probe(struct i2c_client *client) 1255008f1ca5SJean Delvare { 125630485776SAxel Lin struct device *dev = &client->dev; 1257008f1ca5SJean Delvare struct adt7470_data *data; 125830485776SAxel Lin struct device *hwmon_dev; 1259ef67959cSChris Packham int err; 1260008f1ca5SJean Delvare 126130485776SAxel Lin data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); 12629cc7dcc5SGuenter Roeck if (!data) 12639cc7dcc5SGuenter Roeck return -ENOMEM; 1264008f1ca5SJean Delvare 12652f22d5dfSDarrick J. Wong data->num_temp_sensors = -1; 126689fac11cSDarrick J. Wong data->auto_update_interval = AUTO_UPDATE_INTERVAL; 1267ef67959cSChris Packham data->regmap = devm_regmap_init_i2c(client, &adt7470_regmap_config); 1268ef67959cSChris Packham if (IS_ERR(data->regmap)) 1269ef67959cSChris Packham return PTR_ERR(data->regmap); 12702f22d5dfSDarrick J. Wong 1271008f1ca5SJean Delvare i2c_set_clientdata(client, data); 1272008f1ca5SJean Delvare mutex_init(&data->lock); 12736f9703d0SDarrick J. Wong 12746f9703d0SDarrick J. Wong dev_info(&client->dev, "%s chip found\n", client->name); 12756f9703d0SDarrick J. Wong 12766f9703d0SDarrick J. Wong /* Initialize the ADT7470 chip */ 1277ef67959cSChris Packham err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, 1278ef67959cSChris Packham ADT7470_STRT_MASK | ADT7470_TEST_MASK, 1279ef67959cSChris Packham ADT7470_STRT_MASK | ADT7470_TEST_MASK); 1280ef67959cSChris Packham if (err < 0) 1281ef67959cSChris Packham return err; 12826f9703d0SDarrick J. Wong 12836f9703d0SDarrick J. Wong /* Register sysfs hooks */ 1284fc958a61SChris Packham hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, 1285fc958a61SChris Packham &adt7470_chip_info, 128630485776SAxel Lin adt7470_groups); 12876f9703d0SDarrick J. Wong 128830485776SAxel Lin if (IS_ERR(hwmon_dev)) 128930485776SAxel Lin return PTR_ERR(hwmon_dev); 12906f9703d0SDarrick J. Wong 1291f170168bSKees Cook data->auto_update = kthread_run(adt7470_update_thread, client, "%s", 129230485776SAxel Lin dev_name(hwmon_dev)); 129323bd022aSChris Packham if (IS_ERR(data->auto_update)) 129430485776SAxel Lin return PTR_ERR(data->auto_update); 129589fac11cSDarrick J. Wong 12966f9703d0SDarrick J. Wong return 0; 12976f9703d0SDarrick J. Wong } 12986f9703d0SDarrick J. Wong 1299ed5c2f5fSUwe Kleine-König static void adt7470_remove(struct i2c_client *client) 13006f9703d0SDarrick J. Wong { 13016f9703d0SDarrick J. Wong struct adt7470_data *data = i2c_get_clientdata(client); 13026f9703d0SDarrick J. Wong 130389fac11cSDarrick J. Wong kthread_stop(data->auto_update); 13046f9703d0SDarrick J. Wong } 13056f9703d0SDarrick J. Wong 13069027d933SAxel Lin static const struct i2c_device_id adt7470_id[] = { 13079027d933SAxel Lin { "adt7470", 0 }, 13089027d933SAxel Lin { } 13099027d933SAxel Lin }; 13109027d933SAxel Lin MODULE_DEVICE_TABLE(i2c, adt7470_id); 13119027d933SAxel Lin 13129027d933SAxel Lin static struct i2c_driver adt7470_driver = { 13139027d933SAxel Lin .class = I2C_CLASS_HWMON, 13149027d933SAxel Lin .driver = { 13159027d933SAxel Lin .name = "adt7470", 13169027d933SAxel Lin }, 1317*1975d167SUwe Kleine-König .probe = adt7470_probe, 13189027d933SAxel Lin .remove = adt7470_remove, 13199027d933SAxel Lin .id_table = adt7470_id, 13209027d933SAxel Lin .detect = adt7470_detect, 13219027d933SAxel Lin .address_list = normal_i2c, 13229027d933SAxel Lin }; 13239027d933SAxel Lin 1324f0967eeaSAxel Lin module_i2c_driver(adt7470_driver); 13256f9703d0SDarrick J. Wong 13265407e051SDarrick J. Wong MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); 13276f9703d0SDarrick J. Wong MODULE_DESCRIPTION("ADT7470 driver"); 13286f9703d0SDarrick J. Wong MODULE_LICENSE("GPL"); 1329