18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * amc6821.c - Part of lm_sensors, Linux kernel modules for hardware 48c2ecf20Sopenharmony_ci * monitoring 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 T. Mertelj <tomaz.mertelj@guest.arnes.si> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on max6650.c: 88c2ecf20Sopenharmony_ci * Copyright (C) 2007 Hans J. Koch <hjk@hansjkoch.de> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> /* Needed for KERN_INFO */ 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 168c2ecf20Sopenharmony_ci#include <linux/i2c.h> 178c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 188c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 198c2ecf20Sopenharmony_ci#include <linux/err.h> 208c2ecf20Sopenharmony_ci#include <linux/mutex.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Addresses to scan. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = {0x18, 0x19, 0x1a, 0x2c, 0x2d, 0x2e, 278c2ecf20Sopenharmony_ci 0x4c, 0x4d, 0x4e, I2C_CLIENT_END}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * Insmod parameters 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int pwminv; /*Inverted PWM output. */ 348c2ecf20Sopenharmony_cimodule_param(pwminv, int, 0444); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int init = 1; /*Power-on initialization.*/ 378c2ecf20Sopenharmony_cimodule_param(init, int, 0444); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cienum chips { amc6821 }; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define AMC6821_REG_DEV_ID 0x3D 428c2ecf20Sopenharmony_ci#define AMC6821_REG_COMP_ID 0x3E 438c2ecf20Sopenharmony_ci#define AMC6821_REG_CONF1 0x00 448c2ecf20Sopenharmony_ci#define AMC6821_REG_CONF2 0x01 458c2ecf20Sopenharmony_ci#define AMC6821_REG_CONF3 0x3F 468c2ecf20Sopenharmony_ci#define AMC6821_REG_CONF4 0x04 478c2ecf20Sopenharmony_ci#define AMC6821_REG_STAT1 0x02 488c2ecf20Sopenharmony_ci#define AMC6821_REG_STAT2 0x03 498c2ecf20Sopenharmony_ci#define AMC6821_REG_TDATA_LOW 0x08 508c2ecf20Sopenharmony_ci#define AMC6821_REG_TDATA_HI 0x09 518c2ecf20Sopenharmony_ci#define AMC6821_REG_LTEMP_HI 0x0A 528c2ecf20Sopenharmony_ci#define AMC6821_REG_RTEMP_HI 0x0B 538c2ecf20Sopenharmony_ci#define AMC6821_REG_LTEMP_LIMIT_MIN 0x15 548c2ecf20Sopenharmony_ci#define AMC6821_REG_LTEMP_LIMIT_MAX 0x14 558c2ecf20Sopenharmony_ci#define AMC6821_REG_RTEMP_LIMIT_MIN 0x19 568c2ecf20Sopenharmony_ci#define AMC6821_REG_RTEMP_LIMIT_MAX 0x18 578c2ecf20Sopenharmony_ci#define AMC6821_REG_LTEMP_CRIT 0x1B 588c2ecf20Sopenharmony_ci#define AMC6821_REG_RTEMP_CRIT 0x1D 598c2ecf20Sopenharmony_ci#define AMC6821_REG_PSV_TEMP 0x1C 608c2ecf20Sopenharmony_ci#define AMC6821_REG_DCY 0x22 618c2ecf20Sopenharmony_ci#define AMC6821_REG_LTEMP_FAN_CTRL 0x24 628c2ecf20Sopenharmony_ci#define AMC6821_REG_RTEMP_FAN_CTRL 0x25 638c2ecf20Sopenharmony_ci#define AMC6821_REG_DCY_LOW_TEMP 0x21 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define AMC6821_REG_TACH_LLIMITL 0x10 668c2ecf20Sopenharmony_ci#define AMC6821_REG_TACH_LLIMITH 0x11 678c2ecf20Sopenharmony_ci#define AMC6821_REG_TACH_HLIMITL 0x12 688c2ecf20Sopenharmony_ci#define AMC6821_REG_TACH_HLIMITH 0x13 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define AMC6821_CONF1_START 0x01 718c2ecf20Sopenharmony_ci#define AMC6821_CONF1_FAN_INT_EN 0x02 728c2ecf20Sopenharmony_ci#define AMC6821_CONF1_FANIE 0x04 738c2ecf20Sopenharmony_ci#define AMC6821_CONF1_PWMINV 0x08 748c2ecf20Sopenharmony_ci#define AMC6821_CONF1_FAN_FAULT_EN 0x10 758c2ecf20Sopenharmony_ci#define AMC6821_CONF1_FDRC0 0x20 768c2ecf20Sopenharmony_ci#define AMC6821_CONF1_FDRC1 0x40 778c2ecf20Sopenharmony_ci#define AMC6821_CONF1_THERMOVIE 0x80 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define AMC6821_CONF2_PWM_EN 0x01 808c2ecf20Sopenharmony_ci#define AMC6821_CONF2_TACH_MODE 0x02 818c2ecf20Sopenharmony_ci#define AMC6821_CONF2_TACH_EN 0x04 828c2ecf20Sopenharmony_ci#define AMC6821_CONF2_RTFIE 0x08 838c2ecf20Sopenharmony_ci#define AMC6821_CONF2_LTOIE 0x10 848c2ecf20Sopenharmony_ci#define AMC6821_CONF2_RTOIE 0x20 858c2ecf20Sopenharmony_ci#define AMC6821_CONF2_PSVIE 0x40 868c2ecf20Sopenharmony_ci#define AMC6821_CONF2_RST 0x80 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define AMC6821_CONF3_THERM_FAN_EN 0x80 898c2ecf20Sopenharmony_ci#define AMC6821_CONF3_REV_MASK 0x0F 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define AMC6821_CONF4_OVREN 0x10 928c2ecf20Sopenharmony_ci#define AMC6821_CONF4_TACH_FAST 0x20 938c2ecf20Sopenharmony_ci#define AMC6821_CONF4_PSPR 0x40 948c2ecf20Sopenharmony_ci#define AMC6821_CONF4_MODE 0x80 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define AMC6821_STAT1_RPM_ALARM 0x01 978c2ecf20Sopenharmony_ci#define AMC6821_STAT1_FANS 0x02 988c2ecf20Sopenharmony_ci#define AMC6821_STAT1_RTH 0x04 998c2ecf20Sopenharmony_ci#define AMC6821_STAT1_RTL 0x08 1008c2ecf20Sopenharmony_ci#define AMC6821_STAT1_R_THERM 0x10 1018c2ecf20Sopenharmony_ci#define AMC6821_STAT1_RTF 0x20 1028c2ecf20Sopenharmony_ci#define AMC6821_STAT1_LTH 0x40 1038c2ecf20Sopenharmony_ci#define AMC6821_STAT1_LTL 0x80 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define AMC6821_STAT2_RTC 0x08 1068c2ecf20Sopenharmony_ci#define AMC6821_STAT2_LTC 0x10 1078c2ecf20Sopenharmony_ci#define AMC6821_STAT2_LPSV 0x20 1088c2ecf20Sopenharmony_ci#define AMC6821_STAT2_L_THERM 0x40 1098c2ecf20Sopenharmony_ci#define AMC6821_STAT2_THERM_IN 0x80 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cienum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX, 1128c2ecf20Sopenharmony_ci IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN, 1138c2ecf20Sopenharmony_ci IDX_TEMP2_MAX, IDX_TEMP2_CRIT, 1148c2ecf20Sopenharmony_ci TEMP_IDX_LEN, }; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic const u8 temp_reg[] = {AMC6821_REG_LTEMP_HI, 1178c2ecf20Sopenharmony_ci AMC6821_REG_LTEMP_LIMIT_MIN, 1188c2ecf20Sopenharmony_ci AMC6821_REG_LTEMP_LIMIT_MAX, 1198c2ecf20Sopenharmony_ci AMC6821_REG_LTEMP_CRIT, 1208c2ecf20Sopenharmony_ci AMC6821_REG_RTEMP_HI, 1218c2ecf20Sopenharmony_ci AMC6821_REG_RTEMP_LIMIT_MIN, 1228c2ecf20Sopenharmony_ci AMC6821_REG_RTEMP_LIMIT_MAX, 1238c2ecf20Sopenharmony_ci AMC6821_REG_RTEMP_CRIT, }; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cienum {IDX_FAN1_INPUT = 0, IDX_FAN1_MIN, IDX_FAN1_MAX, 1268c2ecf20Sopenharmony_ci FAN1_IDX_LEN, }; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW, 1298c2ecf20Sopenharmony_ci AMC6821_REG_TACH_LLIMITL, 1308c2ecf20Sopenharmony_ci AMC6821_REG_TACH_HLIMITL, }; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI, 1348c2ecf20Sopenharmony_ci AMC6821_REG_TACH_LLIMITH, 1358c2ecf20Sopenharmony_ci AMC6821_REG_TACH_HLIMITH, }; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* 1388c2ecf20Sopenharmony_ci * Client data (each client gets its own) 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistruct amc6821_data { 1428c2ecf20Sopenharmony_ci struct i2c_client *client; 1438c2ecf20Sopenharmony_ci struct mutex update_lock; 1448c2ecf20Sopenharmony_ci char valid; /* zero until following fields are valid */ 1458c2ecf20Sopenharmony_ci unsigned long last_updated; /* in jiffies */ 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* register values */ 1488c2ecf20Sopenharmony_ci int temp[TEMP_IDX_LEN]; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci u16 fan[FAN1_IDX_LEN]; 1518c2ecf20Sopenharmony_ci u8 fan1_div; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci u8 pwm1; 1548c2ecf20Sopenharmony_ci u8 temp1_auto_point_temp[3]; 1558c2ecf20Sopenharmony_ci u8 temp2_auto_point_temp[3]; 1568c2ecf20Sopenharmony_ci u8 pwm1_auto_point_pwm[3]; 1578c2ecf20Sopenharmony_ci u8 pwm1_enable; 1588c2ecf20Sopenharmony_ci u8 pwm1_auto_channels_temp; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci u8 stat1; 1618c2ecf20Sopenharmony_ci u8 stat2; 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic struct amc6821_data *amc6821_update_device(struct device *dev) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct amc6821_data *data = dev_get_drvdata(dev); 1678c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1688c2ecf20Sopenharmony_ci int timeout = HZ; 1698c2ecf20Sopenharmony_ci u8 reg; 1708c2ecf20Sopenharmony_ci int i; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + timeout) || 1758c2ecf20Sopenharmony_ci !data->valid) { 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci for (i = 0; i < TEMP_IDX_LEN; i++) 1788c2ecf20Sopenharmony_ci data->temp[i] = (int8_t)i2c_smbus_read_byte_data( 1798c2ecf20Sopenharmony_ci client, temp_reg[i]); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci data->stat1 = i2c_smbus_read_byte_data(client, 1828c2ecf20Sopenharmony_ci AMC6821_REG_STAT1); 1838c2ecf20Sopenharmony_ci data->stat2 = i2c_smbus_read_byte_data(client, 1848c2ecf20Sopenharmony_ci AMC6821_REG_STAT2); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci data->pwm1 = i2c_smbus_read_byte_data(client, 1878c2ecf20Sopenharmony_ci AMC6821_REG_DCY); 1888c2ecf20Sopenharmony_ci for (i = 0; i < FAN1_IDX_LEN; i++) { 1898c2ecf20Sopenharmony_ci data->fan[i] = i2c_smbus_read_byte_data( 1908c2ecf20Sopenharmony_ci client, 1918c2ecf20Sopenharmony_ci fan_reg_low[i]); 1928c2ecf20Sopenharmony_ci data->fan[i] += i2c_smbus_read_byte_data( 1938c2ecf20Sopenharmony_ci client, 1948c2ecf20Sopenharmony_ci fan_reg_hi[i]) << 8; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci data->fan1_div = i2c_smbus_read_byte_data(client, 1978c2ecf20Sopenharmony_ci AMC6821_REG_CONF4); 1988c2ecf20Sopenharmony_ci data->fan1_div = data->fan1_div & AMC6821_CONF4_PSPR ? 4 : 2; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci data->pwm1_auto_point_pwm[0] = 0; 2018c2ecf20Sopenharmony_ci data->pwm1_auto_point_pwm[2] = 255; 2028c2ecf20Sopenharmony_ci data->pwm1_auto_point_pwm[1] = i2c_smbus_read_byte_data(client, 2038c2ecf20Sopenharmony_ci AMC6821_REG_DCY_LOW_TEMP); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci data->temp1_auto_point_temp[0] = 2068c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, 2078c2ecf20Sopenharmony_ci AMC6821_REG_PSV_TEMP); 2088c2ecf20Sopenharmony_ci data->temp2_auto_point_temp[0] = 2098c2ecf20Sopenharmony_ci data->temp1_auto_point_temp[0]; 2108c2ecf20Sopenharmony_ci reg = i2c_smbus_read_byte_data(client, 2118c2ecf20Sopenharmony_ci AMC6821_REG_LTEMP_FAN_CTRL); 2128c2ecf20Sopenharmony_ci data->temp1_auto_point_temp[1] = (reg & 0xF8) >> 1; 2138c2ecf20Sopenharmony_ci reg &= 0x07; 2148c2ecf20Sopenharmony_ci reg = 0x20 >> reg; 2158c2ecf20Sopenharmony_ci if (reg > 0) 2168c2ecf20Sopenharmony_ci data->temp1_auto_point_temp[2] = 2178c2ecf20Sopenharmony_ci data->temp1_auto_point_temp[1] + 2188c2ecf20Sopenharmony_ci (data->pwm1_auto_point_pwm[2] - 2198c2ecf20Sopenharmony_ci data->pwm1_auto_point_pwm[1]) / reg; 2208c2ecf20Sopenharmony_ci else 2218c2ecf20Sopenharmony_ci data->temp1_auto_point_temp[2] = 255; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci reg = i2c_smbus_read_byte_data(client, 2248c2ecf20Sopenharmony_ci AMC6821_REG_RTEMP_FAN_CTRL); 2258c2ecf20Sopenharmony_ci data->temp2_auto_point_temp[1] = (reg & 0xF8) >> 1; 2268c2ecf20Sopenharmony_ci reg &= 0x07; 2278c2ecf20Sopenharmony_ci reg = 0x20 >> reg; 2288c2ecf20Sopenharmony_ci if (reg > 0) 2298c2ecf20Sopenharmony_ci data->temp2_auto_point_temp[2] = 2308c2ecf20Sopenharmony_ci data->temp2_auto_point_temp[1] + 2318c2ecf20Sopenharmony_ci (data->pwm1_auto_point_pwm[2] - 2328c2ecf20Sopenharmony_ci data->pwm1_auto_point_pwm[1]) / reg; 2338c2ecf20Sopenharmony_ci else 2348c2ecf20Sopenharmony_ci data->temp2_auto_point_temp[2] = 255; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci reg = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1); 2378c2ecf20Sopenharmony_ci reg = (reg >> 5) & 0x3; 2388c2ecf20Sopenharmony_ci switch (reg) { 2398c2ecf20Sopenharmony_ci case 0: /*open loop: software sets pwm1*/ 2408c2ecf20Sopenharmony_ci data->pwm1_auto_channels_temp = 0; 2418c2ecf20Sopenharmony_ci data->pwm1_enable = 1; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case 2: /*closed loop: remote T (temp2)*/ 2448c2ecf20Sopenharmony_ci data->pwm1_auto_channels_temp = 2; 2458c2ecf20Sopenharmony_ci data->pwm1_enable = 2; 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci case 3: /*closed loop: local and remote T (temp2)*/ 2488c2ecf20Sopenharmony_ci data->pwm1_auto_channels_temp = 3; 2498c2ecf20Sopenharmony_ci data->pwm1_enable = 3; 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci case 1: /* 2528c2ecf20Sopenharmony_ci * semi-open loop: software sets rpm, chip controls 2538c2ecf20Sopenharmony_ci * pwm1, currently not implemented 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci data->pwm1_auto_channels_temp = 0; 2568c2ecf20Sopenharmony_ci data->pwm1_enable = 0; 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci data->last_updated = jiffies; 2618c2ecf20Sopenharmony_ci data->valid = 1; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2648c2ecf20Sopenharmony_ci return data; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic ssize_t temp_show(struct device *dev, struct device_attribute *devattr, 2688c2ecf20Sopenharmony_ci char *buf) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 2718c2ecf20Sopenharmony_ci int ix = to_sensor_dev_attr(devattr)->index; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->temp[ix] * 1000); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic ssize_t temp_store(struct device *dev, struct device_attribute *attr, 2778c2ecf20Sopenharmony_ci const char *buf, size_t count) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct amc6821_data *data = dev_get_drvdata(dev); 2808c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 2818c2ecf20Sopenharmony_ci int ix = to_sensor_dev_attr(attr)->index; 2828c2ecf20Sopenharmony_ci long val; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci int ret = kstrtol(buf, 10, &val); 2858c2ecf20Sopenharmony_ci if (ret) 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci val = clamp_val(val / 1000, -128, 127); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2908c2ecf20Sopenharmony_ci data->temp[ix] = val; 2918c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, temp_reg[ix], data->temp[ix])) { 2928c2ecf20Sopenharmony_ci dev_err(&client->dev, "Register write error, aborting.\n"); 2938c2ecf20Sopenharmony_ci count = -EIO; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2968c2ecf20Sopenharmony_ci return count; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic ssize_t temp_alarm_show(struct device *dev, 3008c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 3038c2ecf20Sopenharmony_ci int ix = to_sensor_dev_attr(devattr)->index; 3048c2ecf20Sopenharmony_ci u8 flag; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci switch (ix) { 3078c2ecf20Sopenharmony_ci case IDX_TEMP1_MIN: 3088c2ecf20Sopenharmony_ci flag = data->stat1 & AMC6821_STAT1_LTL; 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci case IDX_TEMP1_MAX: 3118c2ecf20Sopenharmony_ci flag = data->stat1 & AMC6821_STAT1_LTH; 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci case IDX_TEMP1_CRIT: 3148c2ecf20Sopenharmony_ci flag = data->stat2 & AMC6821_STAT2_LTC; 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci case IDX_TEMP2_MIN: 3178c2ecf20Sopenharmony_ci flag = data->stat1 & AMC6821_STAT1_RTL; 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci case IDX_TEMP2_MAX: 3208c2ecf20Sopenharmony_ci flag = data->stat1 & AMC6821_STAT1_RTH; 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci case IDX_TEMP2_CRIT: 3238c2ecf20Sopenharmony_ci flag = data->stat2 & AMC6821_STAT2_RTC; 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci default: 3268c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr->index (%d).\n", ix); 3278c2ecf20Sopenharmony_ci return -EINVAL; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci if (flag) 3308c2ecf20Sopenharmony_ci return sprintf(buf, "1"); 3318c2ecf20Sopenharmony_ci else 3328c2ecf20Sopenharmony_ci return sprintf(buf, "0"); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic ssize_t temp2_fault_show(struct device *dev, 3368c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 3398c2ecf20Sopenharmony_ci if (data->stat1 & AMC6821_STAT1_RTF) 3408c2ecf20Sopenharmony_ci return sprintf(buf, "1"); 3418c2ecf20Sopenharmony_ci else 3428c2ecf20Sopenharmony_ci return sprintf(buf, "0"); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic ssize_t pwm1_show(struct device *dev, struct device_attribute *devattr, 3468c2ecf20Sopenharmony_ci char *buf) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 3498c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->pwm1); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic ssize_t pwm1_store(struct device *dev, 3538c2ecf20Sopenharmony_ci struct device_attribute *devattr, const char *buf, 3548c2ecf20Sopenharmony_ci size_t count) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct amc6821_data *data = dev_get_drvdata(dev); 3578c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 3588c2ecf20Sopenharmony_ci long val; 3598c2ecf20Sopenharmony_ci int ret = kstrtol(buf, 10, &val); 3608c2ecf20Sopenharmony_ci if (ret) 3618c2ecf20Sopenharmony_ci return ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3648c2ecf20Sopenharmony_ci data->pwm1 = clamp_val(val , 0, 255); 3658c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, AMC6821_REG_DCY, data->pwm1); 3668c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 3678c2ecf20Sopenharmony_ci return count; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic ssize_t pwm1_enable_show(struct device *dev, 3718c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 3748c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->pwm1_enable); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic ssize_t pwm1_enable_store(struct device *dev, 3788c2ecf20Sopenharmony_ci struct device_attribute *attr, 3798c2ecf20Sopenharmony_ci const char *buf, size_t count) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct amc6821_data *data = dev_get_drvdata(dev); 3828c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 3838c2ecf20Sopenharmony_ci long val; 3848c2ecf20Sopenharmony_ci int config = kstrtol(buf, 10, &val); 3858c2ecf20Sopenharmony_ci if (config) 3868c2ecf20Sopenharmony_ci return config; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 3898c2ecf20Sopenharmony_ci config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1); 3908c2ecf20Sopenharmony_ci if (config < 0) { 3918c2ecf20Sopenharmony_ci dev_err(&client->dev, 3928c2ecf20Sopenharmony_ci "Error reading configuration register, aborting.\n"); 3938c2ecf20Sopenharmony_ci count = config; 3948c2ecf20Sopenharmony_ci goto unlock; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci switch (val) { 3988c2ecf20Sopenharmony_ci case 1: 3998c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF1_FDRC0; 4008c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF1_FDRC1; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci case 2: 4038c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF1_FDRC0; 4048c2ecf20Sopenharmony_ci config |= AMC6821_CONF1_FDRC1; 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci case 3: 4078c2ecf20Sopenharmony_ci config |= AMC6821_CONF1_FDRC0; 4088c2ecf20Sopenharmony_ci config |= AMC6821_CONF1_FDRC1; 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci default: 4118c2ecf20Sopenharmony_ci count = -EINVAL; 4128c2ecf20Sopenharmony_ci goto unlock; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF1, config)) { 4158c2ecf20Sopenharmony_ci dev_err(&client->dev, 4168c2ecf20Sopenharmony_ci "Configuration register write error, aborting.\n"); 4178c2ecf20Sopenharmony_ci count = -EIO; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ciunlock: 4208c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 4218c2ecf20Sopenharmony_ci return count; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic ssize_t pwm1_auto_channels_temp_show(struct device *dev, 4258c2ecf20Sopenharmony_ci struct device_attribute *devattr, 4268c2ecf20Sopenharmony_ci char *buf) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 4298c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->pwm1_auto_channels_temp); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic ssize_t temp_auto_point_temp_show(struct device *dev, 4338c2ecf20Sopenharmony_ci struct device_attribute *devattr, 4348c2ecf20Sopenharmony_ci char *buf) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci int ix = to_sensor_dev_attr_2(devattr)->index; 4378c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(devattr)->nr; 4388c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 4398c2ecf20Sopenharmony_ci switch (nr) { 4408c2ecf20Sopenharmony_ci case 1: 4418c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 4428c2ecf20Sopenharmony_ci data->temp1_auto_point_temp[ix] * 1000); 4438c2ecf20Sopenharmony_ci case 2: 4448c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 4458c2ecf20Sopenharmony_ci data->temp2_auto_point_temp[ix] * 1000); 4468c2ecf20Sopenharmony_ci default: 4478c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr->nr (%d).\n", nr); 4488c2ecf20Sopenharmony_ci return -EINVAL; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic ssize_t pwm1_auto_point_pwm_show(struct device *dev, 4538c2ecf20Sopenharmony_ci struct device_attribute *devattr, 4548c2ecf20Sopenharmony_ci char *buf) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci int ix = to_sensor_dev_attr(devattr)->index; 4578c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 4588c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[ix]); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic inline ssize_t set_slope_register(struct i2c_client *client, 4628c2ecf20Sopenharmony_ci u8 reg, 4638c2ecf20Sopenharmony_ci u8 dpwm, 4648c2ecf20Sopenharmony_ci u8 *ptemp) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci int dt; 4678c2ecf20Sopenharmony_ci u8 tmp; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci dt = ptemp[2]-ptemp[1]; 4708c2ecf20Sopenharmony_ci for (tmp = 4; tmp > 0; tmp--) { 4718c2ecf20Sopenharmony_ci if (dt * (0x20 >> tmp) >= dpwm) 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci tmp |= (ptemp[1] & 0x7C) << 1; 4758c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, 4768c2ecf20Sopenharmony_ci reg, tmp)) { 4778c2ecf20Sopenharmony_ci dev_err(&client->dev, "Register write error, aborting.\n"); 4788c2ecf20Sopenharmony_ci return -EIO; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic ssize_t temp_auto_point_temp_store(struct device *dev, 4848c2ecf20Sopenharmony_ci struct device_attribute *attr, 4858c2ecf20Sopenharmony_ci const char *buf, size_t count) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 4888c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 4898c2ecf20Sopenharmony_ci int ix = to_sensor_dev_attr_2(attr)->index; 4908c2ecf20Sopenharmony_ci int nr = to_sensor_dev_attr_2(attr)->nr; 4918c2ecf20Sopenharmony_ci u8 *ptemp; 4928c2ecf20Sopenharmony_ci u8 reg; 4938c2ecf20Sopenharmony_ci int dpwm; 4948c2ecf20Sopenharmony_ci long val; 4958c2ecf20Sopenharmony_ci int ret = kstrtol(buf, 10, &val); 4968c2ecf20Sopenharmony_ci if (ret) 4978c2ecf20Sopenharmony_ci return ret; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci switch (nr) { 5008c2ecf20Sopenharmony_ci case 1: 5018c2ecf20Sopenharmony_ci ptemp = data->temp1_auto_point_temp; 5028c2ecf20Sopenharmony_ci reg = AMC6821_REG_LTEMP_FAN_CTRL; 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci case 2: 5058c2ecf20Sopenharmony_ci ptemp = data->temp2_auto_point_temp; 5068c2ecf20Sopenharmony_ci reg = AMC6821_REG_RTEMP_FAN_CTRL; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci default: 5098c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr->nr (%d).\n", nr); 5108c2ecf20Sopenharmony_ci return -EINVAL; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 5148c2ecf20Sopenharmony_ci data->valid = 0; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci switch (ix) { 5178c2ecf20Sopenharmony_ci case 0: 5188c2ecf20Sopenharmony_ci ptemp[0] = clamp_val(val / 1000, 0, 5198c2ecf20Sopenharmony_ci data->temp1_auto_point_temp[1]); 5208c2ecf20Sopenharmony_ci ptemp[0] = clamp_val(ptemp[0], 0, 5218c2ecf20Sopenharmony_ci data->temp2_auto_point_temp[1]); 5228c2ecf20Sopenharmony_ci ptemp[0] = clamp_val(ptemp[0], 0, 63); 5238c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data( 5248c2ecf20Sopenharmony_ci client, 5258c2ecf20Sopenharmony_ci AMC6821_REG_PSV_TEMP, 5268c2ecf20Sopenharmony_ci ptemp[0])) { 5278c2ecf20Sopenharmony_ci dev_err(&client->dev, 5288c2ecf20Sopenharmony_ci "Register write error, aborting.\n"); 5298c2ecf20Sopenharmony_ci count = -EIO; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci goto EXIT; 5328c2ecf20Sopenharmony_ci case 1: 5338c2ecf20Sopenharmony_ci ptemp[1] = clamp_val(val / 1000, (ptemp[0] & 0x7C) + 4, 124); 5348c2ecf20Sopenharmony_ci ptemp[1] &= 0x7C; 5358c2ecf20Sopenharmony_ci ptemp[2] = clamp_val(ptemp[2], ptemp[1] + 1, 255); 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci case 2: 5388c2ecf20Sopenharmony_ci ptemp[2] = clamp_val(val / 1000, ptemp[1]+1, 255); 5398c2ecf20Sopenharmony_ci break; 5408c2ecf20Sopenharmony_ci default: 5418c2ecf20Sopenharmony_ci dev_dbg(dev, "Unknown attr->index (%d).\n", ix); 5428c2ecf20Sopenharmony_ci count = -EINVAL; 5438c2ecf20Sopenharmony_ci goto EXIT; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1]; 5468c2ecf20Sopenharmony_ci if (set_slope_register(client, reg, dpwm, ptemp)) 5478c2ecf20Sopenharmony_ci count = -EIO; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ciEXIT: 5508c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 5518c2ecf20Sopenharmony_ci return count; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic ssize_t pwm1_auto_point_pwm_store(struct device *dev, 5558c2ecf20Sopenharmony_ci struct device_attribute *attr, 5568c2ecf20Sopenharmony_ci const char *buf, size_t count) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct amc6821_data *data = dev_get_drvdata(dev); 5598c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 5608c2ecf20Sopenharmony_ci int dpwm; 5618c2ecf20Sopenharmony_ci long val; 5628c2ecf20Sopenharmony_ci int ret = kstrtol(buf, 10, &val); 5638c2ecf20Sopenharmony_ci if (ret) 5648c2ecf20Sopenharmony_ci return ret; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 5678c2ecf20Sopenharmony_ci data->pwm1_auto_point_pwm[1] = clamp_val(val, 0, 254); 5688c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, AMC6821_REG_DCY_LOW_TEMP, 5698c2ecf20Sopenharmony_ci data->pwm1_auto_point_pwm[1])) { 5708c2ecf20Sopenharmony_ci dev_err(&client->dev, "Register write error, aborting.\n"); 5718c2ecf20Sopenharmony_ci count = -EIO; 5728c2ecf20Sopenharmony_ci goto EXIT; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1]; 5758c2ecf20Sopenharmony_ci if (set_slope_register(client, AMC6821_REG_LTEMP_FAN_CTRL, dpwm, 5768c2ecf20Sopenharmony_ci data->temp1_auto_point_temp)) { 5778c2ecf20Sopenharmony_ci count = -EIO; 5788c2ecf20Sopenharmony_ci goto EXIT; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci if (set_slope_register(client, AMC6821_REG_RTEMP_FAN_CTRL, dpwm, 5818c2ecf20Sopenharmony_ci data->temp2_auto_point_temp)) { 5828c2ecf20Sopenharmony_ci count = -EIO; 5838c2ecf20Sopenharmony_ci goto EXIT; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ciEXIT: 5878c2ecf20Sopenharmony_ci data->valid = 0; 5888c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 5898c2ecf20Sopenharmony_ci return count; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic ssize_t fan_show(struct device *dev, struct device_attribute *devattr, 5938c2ecf20Sopenharmony_ci char *buf) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 5968c2ecf20Sopenharmony_ci int ix = to_sensor_dev_attr(devattr)->index; 5978c2ecf20Sopenharmony_ci if (0 == data->fan[ix]) 5988c2ecf20Sopenharmony_ci return sprintf(buf, "0"); 5998c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (int)(6000000 / data->fan[ix])); 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic ssize_t fan1_fault_show(struct device *dev, 6038c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 6068c2ecf20Sopenharmony_ci if (data->stat1 & AMC6821_STAT1_FANS) 6078c2ecf20Sopenharmony_ci return sprintf(buf, "1"); 6088c2ecf20Sopenharmony_ci else 6098c2ecf20Sopenharmony_ci return sprintf(buf, "0"); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic ssize_t fan_store(struct device *dev, struct device_attribute *attr, 6138c2ecf20Sopenharmony_ci const char *buf, size_t count) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci struct amc6821_data *data = dev_get_drvdata(dev); 6168c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 6178c2ecf20Sopenharmony_ci long val; 6188c2ecf20Sopenharmony_ci int ix = to_sensor_dev_attr(attr)->index; 6198c2ecf20Sopenharmony_ci int ret = kstrtol(buf, 10, &val); 6208c2ecf20Sopenharmony_ci if (ret) 6218c2ecf20Sopenharmony_ci return ret; 6228c2ecf20Sopenharmony_ci val = 1 > val ? 0xFFFF : 6000000/val; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 6258c2ecf20Sopenharmony_ci data->fan[ix] = (u16) clamp_val(val, 1, 0xFFFF); 6268c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, fan_reg_low[ix], 6278c2ecf20Sopenharmony_ci data->fan[ix] & 0xFF)) { 6288c2ecf20Sopenharmony_ci dev_err(&client->dev, "Register write error, aborting.\n"); 6298c2ecf20Sopenharmony_ci count = -EIO; 6308c2ecf20Sopenharmony_ci goto EXIT; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, 6338c2ecf20Sopenharmony_ci fan_reg_hi[ix], data->fan[ix] >> 8)) { 6348c2ecf20Sopenharmony_ci dev_err(&client->dev, "Register write error, aborting.\n"); 6358c2ecf20Sopenharmony_ci count = -EIO; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ciEXIT: 6388c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 6398c2ecf20Sopenharmony_ci return count; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic ssize_t fan1_div_show(struct device *dev, 6438c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct amc6821_data *data = amc6821_update_device(dev); 6468c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->fan1_div); 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic ssize_t fan1_div_store(struct device *dev, 6508c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 6518c2ecf20Sopenharmony_ci size_t count) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct amc6821_data *data = dev_get_drvdata(dev); 6548c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 6558c2ecf20Sopenharmony_ci long val; 6568c2ecf20Sopenharmony_ci int config = kstrtol(buf, 10, &val); 6578c2ecf20Sopenharmony_ci if (config) 6588c2ecf20Sopenharmony_ci return config; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 6618c2ecf20Sopenharmony_ci config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4); 6628c2ecf20Sopenharmony_ci if (config < 0) { 6638c2ecf20Sopenharmony_ci dev_err(&client->dev, 6648c2ecf20Sopenharmony_ci "Error reading configuration register, aborting.\n"); 6658c2ecf20Sopenharmony_ci count = config; 6668c2ecf20Sopenharmony_ci goto EXIT; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci switch (val) { 6698c2ecf20Sopenharmony_ci case 2: 6708c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF4_PSPR; 6718c2ecf20Sopenharmony_ci data->fan1_div = 2; 6728c2ecf20Sopenharmony_ci break; 6738c2ecf20Sopenharmony_ci case 4: 6748c2ecf20Sopenharmony_ci config |= AMC6821_CONF4_PSPR; 6758c2ecf20Sopenharmony_ci data->fan1_div = 4; 6768c2ecf20Sopenharmony_ci break; 6778c2ecf20Sopenharmony_ci default: 6788c2ecf20Sopenharmony_ci count = -EINVAL; 6798c2ecf20Sopenharmony_ci goto EXIT; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, config)) { 6828c2ecf20Sopenharmony_ci dev_err(&client->dev, 6838c2ecf20Sopenharmony_ci "Configuration register write error, aborting.\n"); 6848c2ecf20Sopenharmony_ci count = -EIO; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ciEXIT: 6878c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 6888c2ecf20Sopenharmony_ci return count; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, temp, IDX_TEMP1_INPUT); 6928c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_min, temp, IDX_TEMP1_MIN); 6938c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_max, temp, IDX_TEMP1_MAX); 6948c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, IDX_TEMP1_CRIT); 6958c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, temp_alarm, IDX_TEMP1_MIN); 6968c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, temp_alarm, IDX_TEMP1_MAX); 6978c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, temp_alarm, IDX_TEMP1_CRIT); 6988c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_input, temp, IDX_TEMP2_INPUT); 6998c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_min, temp, IDX_TEMP2_MIN); 7008c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_max, temp, IDX_TEMP2_MAX); 7018c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, IDX_TEMP2_CRIT); 7028c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_fault, temp2_fault, 0); 7038c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, temp_alarm, IDX_TEMP2_MIN); 7048c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, temp_alarm, IDX_TEMP2_MAX); 7058c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, temp_alarm, IDX_TEMP2_CRIT); 7068c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_input, fan, IDX_FAN1_INPUT); 7078c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_min, fan, IDX_FAN1_MIN); 7088c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_max, fan, IDX_FAN1_MAX); 7098c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_fault, fan1_fault, 0); 7108c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_div, fan1_div, 0); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm1, pwm1, 0); 7138c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm1_enable, 0); 7148c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(pwm1_auto_point1_pwm, pwm1_auto_point_pwm, 0); 7158c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_pwm, pwm1_auto_point_pwm, 1); 7168c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm1_auto_point_pwm, 2); 7178c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(pwm1_auto_channels_temp, pwm1_auto_channels_temp, 7188c2ecf20Sopenharmony_ci 0); 7198c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(temp1_auto_point1_temp, temp_auto_point_temp, 7208c2ecf20Sopenharmony_ci 1, 0); 7218c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point2_temp, temp_auto_point_temp, 7228c2ecf20Sopenharmony_ci 1, 1); 7238c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point3_temp, temp_auto_point_temp, 7248c2ecf20Sopenharmony_ci 1, 2); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point1_temp, temp_auto_point_temp, 7278c2ecf20Sopenharmony_ci 2, 0); 7288c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point2_temp, temp_auto_point_temp, 7298c2ecf20Sopenharmony_ci 2, 1); 7308c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point3_temp, temp_auto_point_temp, 7318c2ecf20Sopenharmony_ci 2, 2); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic struct attribute *amc6821_attrs[] = { 7348c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 7358c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_min.dev_attr.attr, 7368c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max.dev_attr.attr, 7378c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit.dev_attr.attr, 7388c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, 7398c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, 7408c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, 7418c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_input.dev_attr.attr, 7428c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_min.dev_attr.attr, 7438c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_max.dev_attr.attr, 7448c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_crit.dev_attr.attr, 7458c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, 7468c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, 7478c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, 7488c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_fault.dev_attr.attr, 7498c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_input.dev_attr.attr, 7508c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_min.dev_attr.attr, 7518c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_max.dev_attr.attr, 7528c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_fault.dev_attr.attr, 7538c2ecf20Sopenharmony_ci &sensor_dev_attr_fan1_div.dev_attr.attr, 7548c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1.dev_attr.attr, 7558c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1_enable.dev_attr.attr, 7568c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, 7578c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, 7588c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, 7598c2ecf20Sopenharmony_ci &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, 7608c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, 7618c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr, 7628c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_auto_point3_temp.dev_attr.attr, 7638c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr, 7648c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr, 7658c2ecf20Sopenharmony_ci &sensor_dev_attr_temp2_auto_point3_temp.dev_attr.attr, 7668c2ecf20Sopenharmony_ci NULL 7678c2ecf20Sopenharmony_ci}; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(amc6821); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */ 7728c2ecf20Sopenharmony_cistatic int amc6821_detect( 7738c2ecf20Sopenharmony_ci struct i2c_client *client, 7748c2ecf20Sopenharmony_ci struct i2c_board_info *info) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 7778c2ecf20Sopenharmony_ci int address = client->addr; 7788c2ecf20Sopenharmony_ci int dev_id, comp_id; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, "amc6821_detect called.\n"); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { 7838c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, 7848c2ecf20Sopenharmony_ci "amc6821: I2C bus doesn't support byte mode, " 7858c2ecf20Sopenharmony_ci "skipping.\n"); 7868c2ecf20Sopenharmony_ci return -ENODEV; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci dev_id = i2c_smbus_read_byte_data(client, AMC6821_REG_DEV_ID); 7908c2ecf20Sopenharmony_ci comp_id = i2c_smbus_read_byte_data(client, AMC6821_REG_COMP_ID); 7918c2ecf20Sopenharmony_ci if (dev_id != 0x21 || comp_id != 0x49) { 7928c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, 7938c2ecf20Sopenharmony_ci "amc6821: detection failed at 0x%02x.\n", 7948c2ecf20Sopenharmony_ci address); 7958c2ecf20Sopenharmony_ci return -ENODEV; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* 7998c2ecf20Sopenharmony_ci * Bit 7 of the address register is ignored, so we can check the 8008c2ecf20Sopenharmony_ci * ID registers again 8018c2ecf20Sopenharmony_ci */ 8028c2ecf20Sopenharmony_ci dev_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_DEV_ID); 8038c2ecf20Sopenharmony_ci comp_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_COMP_ID); 8048c2ecf20Sopenharmony_ci if (dev_id != 0x21 || comp_id != 0x49) { 8058c2ecf20Sopenharmony_ci dev_dbg(&adapter->dev, 8068c2ecf20Sopenharmony_ci "amc6821: detection failed at 0x%02x.\n", 8078c2ecf20Sopenharmony_ci address); 8088c2ecf20Sopenharmony_ci return -ENODEV; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci dev_info(&adapter->dev, "amc6821: chip found at 0x%02x.\n", address); 8128c2ecf20Sopenharmony_ci strlcpy(info->type, "amc6821", I2C_NAME_SIZE); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci return 0; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic int amc6821_init_client(struct i2c_client *client) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci int config; 8208c2ecf20Sopenharmony_ci int err = -EIO; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (init) { 8238c2ecf20Sopenharmony_ci config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (config < 0) { 8268c2ecf20Sopenharmony_ci dev_err(&client->dev, 8278c2ecf20Sopenharmony_ci "Error reading configuration register, aborting.\n"); 8288c2ecf20Sopenharmony_ci return err; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci config |= AMC6821_CONF4_MODE; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, 8348c2ecf20Sopenharmony_ci config)) { 8358c2ecf20Sopenharmony_ci dev_err(&client->dev, 8368c2ecf20Sopenharmony_ci "Configuration register write error, aborting.\n"); 8378c2ecf20Sopenharmony_ci return err; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF3); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (config < 0) { 8438c2ecf20Sopenharmony_ci dev_err(&client->dev, 8448c2ecf20Sopenharmony_ci "Error reading configuration register, aborting.\n"); 8458c2ecf20Sopenharmony_ci return err; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci dev_info(&client->dev, "Revision %d\n", config & 0x0f); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF3_THERM_FAN_EN; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF3, 8538c2ecf20Sopenharmony_ci config)) { 8548c2ecf20Sopenharmony_ci dev_err(&client->dev, 8558c2ecf20Sopenharmony_ci "Configuration register write error, aborting.\n"); 8568c2ecf20Sopenharmony_ci return err; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF2); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (config < 0) { 8628c2ecf20Sopenharmony_ci dev_err(&client->dev, 8638c2ecf20Sopenharmony_ci "Error reading configuration register, aborting.\n"); 8648c2ecf20Sopenharmony_ci return err; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF2_RTFIE; 8688c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF2_LTOIE; 8698c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF2_RTOIE; 8708c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data(client, 8718c2ecf20Sopenharmony_ci AMC6821_REG_CONF2, config)) { 8728c2ecf20Sopenharmony_ci dev_err(&client->dev, 8738c2ecf20Sopenharmony_ci "Configuration register write error, aborting.\n"); 8748c2ecf20Sopenharmony_ci return err; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (config < 0) { 8808c2ecf20Sopenharmony_ci dev_err(&client->dev, 8818c2ecf20Sopenharmony_ci "Error reading configuration register, aborting.\n"); 8828c2ecf20Sopenharmony_ci return err; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF1_THERMOVIE; 8868c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF1_FANIE; 8878c2ecf20Sopenharmony_ci config |= AMC6821_CONF1_START; 8888c2ecf20Sopenharmony_ci if (pwminv) 8898c2ecf20Sopenharmony_ci config |= AMC6821_CONF1_PWMINV; 8908c2ecf20Sopenharmony_ci else 8918c2ecf20Sopenharmony_ci config &= ~AMC6821_CONF1_PWMINV; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if (i2c_smbus_write_byte_data( 8948c2ecf20Sopenharmony_ci client, AMC6821_REG_CONF1, config)) { 8958c2ecf20Sopenharmony_ci dev_err(&client->dev, 8968c2ecf20Sopenharmony_ci "Configuration register write error, aborting.\n"); 8978c2ecf20Sopenharmony_ci return err; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci return 0; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic int amc6821_probe(struct i2c_client *client) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 9068c2ecf20Sopenharmony_ci struct amc6821_data *data; 9078c2ecf20Sopenharmony_ci struct device *hwmon_dev; 9088c2ecf20Sopenharmony_ci int err; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct amc6821_data), GFP_KERNEL); 9118c2ecf20Sopenharmony_ci if (!data) 9128c2ecf20Sopenharmony_ci return -ENOMEM; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci data->client = client; 9158c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* 9188c2ecf20Sopenharmony_ci * Initialize the amc6821 chip 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_ci err = amc6821_init_client(client); 9218c2ecf20Sopenharmony_ci if (err) 9228c2ecf20Sopenharmony_ci return err; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 9258c2ecf20Sopenharmony_ci data, 9268c2ecf20Sopenharmony_ci amc6821_groups); 9278c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic const struct i2c_device_id amc6821_id[] = { 9318c2ecf20Sopenharmony_ci { "amc6821", amc6821 }, 9328c2ecf20Sopenharmony_ci { } 9338c2ecf20Sopenharmony_ci}; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, amc6821_id); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic struct i2c_driver amc6821_driver = { 9388c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 9398c2ecf20Sopenharmony_ci .driver = { 9408c2ecf20Sopenharmony_ci .name = "amc6821", 9418c2ecf20Sopenharmony_ci }, 9428c2ecf20Sopenharmony_ci .probe_new = amc6821_probe, 9438c2ecf20Sopenharmony_ci .id_table = amc6821_id, 9448c2ecf20Sopenharmony_ci .detect = amc6821_detect, 9458c2ecf20Sopenharmony_ci .address_list = normal_i2c, 9468c2ecf20Sopenharmony_ci}; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cimodule_i2c_driver(amc6821_driver); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 9518c2ecf20Sopenharmony_ciMODULE_AUTHOR("T. Mertelj <tomaz.mertelj@guest.arnes.si>"); 9528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments amc6821 hwmon driver"); 953