18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for SMM665 Power Controller / Monitor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Ericsson AB. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This driver should also work for SMM465, SMM764, and SMM766, but is untested 88c2ecf20Sopenharmony_ci * for those chips. Only monitoring functionality is implemented. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Datasheets: 118c2ecf20Sopenharmony_ci * http://www.summitmicro.com/prod_select/summary/SMM665/SMM665B_2089_20.pdf 128c2ecf20Sopenharmony_ci * http://www.summitmicro.com/prod_select/summary/SMM766B/SMM766B_2122.pdf 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/i2c.h> 218c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 228c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Internal reference voltage (VREF, x 1000 */ 278c2ecf20Sopenharmony_ci#define SMM665_VREF_ADC_X1000 1250 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* module parameters */ 308c2ecf20Sopenharmony_cistatic int vref = SMM665_VREF_ADC_X1000; 318c2ecf20Sopenharmony_cimodule_param(vref, int, 0); 328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vref, "Reference voltage in mV"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cienum chips { smm465, smm665, smm665c, smm764, smm766 }; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * ADC channel addresses 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_A 0x00 408c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_B 0x01 418c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_C 0x02 428c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_D 0x03 438c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_E 0x04 448c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_F 0x05 458c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_VDD 0x06 468c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_12V 0x07 478c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_INT_TEMP 0x08 488c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_AIN1 0x09 498c2ecf20Sopenharmony_ci#define SMM665_MISC16_ADC_DATA_AIN2 0x0a 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * Command registers 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci#define SMM665_MISC8_CMD_STS 0x80 558c2ecf20Sopenharmony_ci#define SMM665_MISC8_STATUS1 0x81 568c2ecf20Sopenharmony_ci#define SMM665_MISC8_STATUSS2 0x82 578c2ecf20Sopenharmony_ci#define SMM665_MISC8_IO_POLARITY 0x83 588c2ecf20Sopenharmony_ci#define SMM665_MISC8_PUP_POLARITY 0x84 598c2ecf20Sopenharmony_ci#define SMM665_MISC8_ADOC_STATUS1 0x85 608c2ecf20Sopenharmony_ci#define SMM665_MISC8_ADOC_STATUS2 0x86 618c2ecf20Sopenharmony_ci#define SMM665_MISC8_WRITE_PROT 0x87 628c2ecf20Sopenharmony_ci#define SMM665_MISC8_STS_TRACK 0x88 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * Configuration registers and register groups 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci#define SMM665_ADOC_ENABLE 0x0d 688c2ecf20Sopenharmony_ci#define SMM665_LIMIT_BASE 0x80 /* First limit register */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci * Limit register bit masks 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci#define SMM665_TRIGGER_RST 0x8000 748c2ecf20Sopenharmony_ci#define SMM665_TRIGGER_HEALTHY 0x4000 758c2ecf20Sopenharmony_ci#define SMM665_TRIGGER_POWEROFF 0x2000 768c2ecf20Sopenharmony_ci#define SMM665_TRIGGER_SHUTDOWN 0x1000 778c2ecf20Sopenharmony_ci#define SMM665_ADC_MASK 0x03ff 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define smm665_is_critical(lim) ((lim) & (SMM665_TRIGGER_RST \ 808c2ecf20Sopenharmony_ci | SMM665_TRIGGER_POWEROFF \ 818c2ecf20Sopenharmony_ci | SMM665_TRIGGER_SHUTDOWN)) 828c2ecf20Sopenharmony_ci/* 838c2ecf20Sopenharmony_ci * Fault register bit definitions 848c2ecf20Sopenharmony_ci * Values are merged from status registers 1/2, 858c2ecf20Sopenharmony_ci * with status register 1 providing the upper 8 bits. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci#define SMM665_FAULT_A 0x0001 888c2ecf20Sopenharmony_ci#define SMM665_FAULT_B 0x0002 898c2ecf20Sopenharmony_ci#define SMM665_FAULT_C 0x0004 908c2ecf20Sopenharmony_ci#define SMM665_FAULT_D 0x0008 918c2ecf20Sopenharmony_ci#define SMM665_FAULT_E 0x0010 928c2ecf20Sopenharmony_ci#define SMM665_FAULT_F 0x0020 938c2ecf20Sopenharmony_ci#define SMM665_FAULT_VDD 0x0040 948c2ecf20Sopenharmony_ci#define SMM665_FAULT_12V 0x0080 958c2ecf20Sopenharmony_ci#define SMM665_FAULT_TEMP 0x0100 968c2ecf20Sopenharmony_ci#define SMM665_FAULT_AIN1 0x0200 978c2ecf20Sopenharmony_ci#define SMM665_FAULT_AIN2 0x0400 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * I2C Register addresses 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * The configuration register needs to be the configured base register. 1038c2ecf20Sopenharmony_ci * The command/status register address is derived from it. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci#define SMM665_REGMASK 0x78 1068c2ecf20Sopenharmony_ci#define SMM665_CMDREG_BASE 0x48 1078c2ecf20Sopenharmony_ci#define SMM665_CONFREG_BASE 0x50 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* 1108c2ecf20Sopenharmony_ci * Equations given by chip manufacturer to calculate voltage/temperature values 1118c2ecf20Sopenharmony_ci * vref = Reference voltage on VREF_ADC pin (module parameter) 1128c2ecf20Sopenharmony_ci * adc = 10bit ADC value read back from registers 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* Voltage A-F and VDD */ 1168c2ecf20Sopenharmony_ci#define SMM665_VMON_ADC_TO_VOLTS(adc) ((adc) * vref / 256) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* Voltage 12VIN */ 1198c2ecf20Sopenharmony_ci#define SMM665_12VIN_ADC_TO_VOLTS(adc) ((adc) * vref * 3 / 256) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* Voltage AIN1, AIN2 */ 1228c2ecf20Sopenharmony_ci#define SMM665_AIN_ADC_TO_VOLTS(adc) ((adc) * vref / 512) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* Temp Sensor */ 1258c2ecf20Sopenharmony_ci#define SMM665_TEMP_ADC_TO_CELSIUS(adc) (((adc) <= 511) ? \ 1268c2ecf20Sopenharmony_ci ((int)(adc) * 1000 / 4) : \ 1278c2ecf20Sopenharmony_ci (((int)(adc) - 0x400) * 1000 / 4)) 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define SMM665_NUM_ADC 11 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * Chip dependent ADC conversion time, in uS 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci#define SMM665_ADC_WAIT_SMM665 70 1358c2ecf20Sopenharmony_ci#define SMM665_ADC_WAIT_SMM766 185 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistruct smm665_data { 1388c2ecf20Sopenharmony_ci enum chips type; 1398c2ecf20Sopenharmony_ci int conversion_time; /* ADC conversion time */ 1408c2ecf20Sopenharmony_ci struct i2c_client *client; 1418c2ecf20Sopenharmony_ci struct mutex update_lock; 1428c2ecf20Sopenharmony_ci bool valid; 1438c2ecf20Sopenharmony_ci unsigned long last_updated; /* in jiffies */ 1448c2ecf20Sopenharmony_ci u16 adc[SMM665_NUM_ADC]; /* adc values (raw) */ 1458c2ecf20Sopenharmony_ci u16 faults; /* fault status */ 1468c2ecf20Sopenharmony_ci /* The following values are in mV */ 1478c2ecf20Sopenharmony_ci int critical_min_limit[SMM665_NUM_ADC]; 1488c2ecf20Sopenharmony_ci int alarm_min_limit[SMM665_NUM_ADC]; 1498c2ecf20Sopenharmony_ci int critical_max_limit[SMM665_NUM_ADC]; 1508c2ecf20Sopenharmony_ci int alarm_max_limit[SMM665_NUM_ADC]; 1518c2ecf20Sopenharmony_ci struct i2c_client *cmdreg; 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * smm665_read16() 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * Read 16 bit value from <reg>, <reg+1>. Upper 8 bits are in <reg>. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cistatic int smm665_read16(struct i2c_client *client, int reg) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int rv, val; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci rv = i2c_smbus_read_byte_data(client, reg); 1648c2ecf20Sopenharmony_ci if (rv < 0) 1658c2ecf20Sopenharmony_ci return rv; 1668c2ecf20Sopenharmony_ci val = rv << 8; 1678c2ecf20Sopenharmony_ci rv = i2c_smbus_read_byte_data(client, reg + 1); 1688c2ecf20Sopenharmony_ci if (rv < 0) 1698c2ecf20Sopenharmony_ci return rv; 1708c2ecf20Sopenharmony_ci val |= rv; 1718c2ecf20Sopenharmony_ci return val; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* 1758c2ecf20Sopenharmony_ci * Read adc value. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistatic int smm665_read_adc(struct smm665_data *data, int adc) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct i2c_client *client = data->cmdreg; 1808c2ecf20Sopenharmony_ci int rv; 1818c2ecf20Sopenharmony_ci int radc; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * Algorithm for reading ADC, per SMM665 datasheet 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * {[S][addr][W][Ack]} {[offset][Ack]} {[S][addr][R][Nack]} 1878c2ecf20Sopenharmony_ci * [wait conversion time] 1888c2ecf20Sopenharmony_ci * {[S][addr][R][Ack]} {[datahi][Ack]} {[datalo][Ack][P]} 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * To implement the first part of this exchange, 1918c2ecf20Sopenharmony_ci * do a full read transaction and expect a failure/Nack. 1928c2ecf20Sopenharmony_ci * This sets up the address pointer on the SMM665 1938c2ecf20Sopenharmony_ci * and starts the ADC conversion. 1948c2ecf20Sopenharmony_ci * Then do a two-byte read transaction. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci rv = i2c_smbus_read_byte_data(client, adc << 3); 1978c2ecf20Sopenharmony_ci if (rv != -ENXIO) { 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * We expect ENXIO to reflect NACK 2008c2ecf20Sopenharmony_ci * (per Documentation/i2c/fault-codes.rst). 2018c2ecf20Sopenharmony_ci * Everything else is an error. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci dev_dbg(&client->dev, 2048c2ecf20Sopenharmony_ci "Unexpected return code %d when setting ADC index", rv); 2058c2ecf20Sopenharmony_ci return (rv < 0) ? rv : -EIO; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci udelay(data->conversion_time); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * Now read two bytes. 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * Neither i2c_smbus_read_byte() nor 2148c2ecf20Sopenharmony_ci * i2c_smbus_read_block_data() worked here, 2158c2ecf20Sopenharmony_ci * so use i2c_smbus_read_word_swapped() instead. 2168c2ecf20Sopenharmony_ci * We could also try to use i2c_master_recv(), 2178c2ecf20Sopenharmony_ci * but that is not always supported. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci rv = i2c_smbus_read_word_swapped(client, 0); 2208c2ecf20Sopenharmony_ci if (rv < 0) { 2218c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "Failed to read ADC value: error %d", rv); 2228c2ecf20Sopenharmony_ci return rv; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * Validate/verify readback adc channel (in bit 11..14). 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci radc = (rv >> 11) & 0x0f; 2288c2ecf20Sopenharmony_ci if (radc != adc) { 2298c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "Unexpected RADC: Expected %d got %d", 2308c2ecf20Sopenharmony_ci adc, radc); 2318c2ecf20Sopenharmony_ci return -EIO; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return rv & SMM665_ADC_MASK; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic struct smm665_data *smm665_update_device(struct device *dev) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct smm665_data *data = dev_get_drvdata(dev); 2408c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 2418c2ecf20Sopenharmony_ci struct smm665_data *ret = data; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { 2468c2ecf20Sopenharmony_ci int i, val; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* 2498c2ecf20Sopenharmony_ci * read status registers 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci val = smm665_read16(client, SMM665_MISC8_STATUS1); 2528c2ecf20Sopenharmony_ci if (unlikely(val < 0)) { 2538c2ecf20Sopenharmony_ci ret = ERR_PTR(val); 2548c2ecf20Sopenharmony_ci goto abort; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci data->faults = val; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* Read adc registers */ 2598c2ecf20Sopenharmony_ci for (i = 0; i < SMM665_NUM_ADC; i++) { 2608c2ecf20Sopenharmony_ci val = smm665_read_adc(data, i); 2618c2ecf20Sopenharmony_ci if (unlikely(val < 0)) { 2628c2ecf20Sopenharmony_ci ret = ERR_PTR(val); 2638c2ecf20Sopenharmony_ci goto abort; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci data->adc[i] = val; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci data->last_updated = jiffies; 2688c2ecf20Sopenharmony_ci data->valid = 1; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ciabort: 2718c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 2728c2ecf20Sopenharmony_ci return ret; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* Return converted value from given adc */ 2768c2ecf20Sopenharmony_cistatic int smm665_convert(u16 adcval, int index) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci int val = 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci switch (index) { 2818c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_12V: 2828c2ecf20Sopenharmony_ci val = SMM665_12VIN_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_VDD: 2868c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_A: 2878c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_B: 2888c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_C: 2898c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_D: 2908c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_E: 2918c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_F: 2928c2ecf20Sopenharmony_ci val = SMM665_VMON_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK); 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_AIN1: 2968c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_AIN2: 2978c2ecf20Sopenharmony_ci val = SMM665_AIN_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK); 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci case SMM665_MISC16_ADC_DATA_INT_TEMP: 3018c2ecf20Sopenharmony_ci val = SMM665_TEMP_ADC_TO_CELSIUS(adcval & SMM665_ADC_MASK); 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci default: 3058c2ecf20Sopenharmony_ci /* If we get here, the developer messed up */ 3068c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return val; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int smm665_get_min(struct device *dev, int index) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct smm665_data *data = dev_get_drvdata(dev); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return data->alarm_min_limit[index]; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int smm665_get_max(struct device *dev, int index) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct smm665_data *data = dev_get_drvdata(dev); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return data->alarm_max_limit[index]; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int smm665_get_lcrit(struct device *dev, int index) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct smm665_data *data = dev_get_drvdata(dev); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return data->critical_min_limit[index]; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int smm665_get_crit(struct device *dev, int index) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct smm665_data *data = dev_get_drvdata(dev); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return data->critical_max_limit[index]; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic ssize_t smm665_show_crit_alarm(struct device *dev, 3428c2ecf20Sopenharmony_ci struct device_attribute *da, char *buf) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 3458c2ecf20Sopenharmony_ci struct smm665_data *data = smm665_update_device(dev); 3468c2ecf20Sopenharmony_ci int val = 0; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (IS_ERR(data)) 3498c2ecf20Sopenharmony_ci return PTR_ERR(data); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (data->faults & (1 << attr->index)) 3528c2ecf20Sopenharmony_ci val = 1; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", val); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic ssize_t smm665_show_input(struct device *dev, 3588c2ecf20Sopenharmony_ci struct device_attribute *da, char *buf) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 3618c2ecf20Sopenharmony_ci struct smm665_data *data = smm665_update_device(dev); 3628c2ecf20Sopenharmony_ci int adc = attr->index; 3638c2ecf20Sopenharmony_ci int val; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (IS_ERR(data)) 3668c2ecf20Sopenharmony_ci return PTR_ERR(data); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci val = smm665_convert(data->adc[adc], adc); 3698c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", val); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci#define SMM665_SHOW(what) \ 3738c2ecf20Sopenharmony_cistatic ssize_t smm665_show_##what(struct device *dev, \ 3748c2ecf20Sopenharmony_ci struct device_attribute *da, char *buf) \ 3758c2ecf20Sopenharmony_ci{ \ 3768c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ 3778c2ecf20Sopenharmony_ci const int val = smm665_get_##what(dev, attr->index); \ 3788c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", val); \ 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciSMM665_SHOW(min); 3828c2ecf20Sopenharmony_ciSMM665_SHOW(max); 3838c2ecf20Sopenharmony_ciSMM665_SHOW(lcrit); 3848c2ecf20Sopenharmony_ciSMM665_SHOW(crit); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * These macros are used below in constructing device attribute objects 3888c2ecf20Sopenharmony_ci * for use with sysfs_create_group() to make a sysfs device file 3898c2ecf20Sopenharmony_ci * for each register. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci#define SMM665_ATTR(name, type, cmd_idx) \ 3938c2ecf20Sopenharmony_ci static SENSOR_DEVICE_ATTR(name##_##type, S_IRUGO, \ 3948c2ecf20Sopenharmony_ci smm665_show_##type, NULL, cmd_idx) 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/* Construct a sensor_device_attribute structure for each register */ 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* Input voltages */ 3998c2ecf20Sopenharmony_ciSMM665_ATTR(in1, input, SMM665_MISC16_ADC_DATA_12V); 4008c2ecf20Sopenharmony_ciSMM665_ATTR(in2, input, SMM665_MISC16_ADC_DATA_VDD); 4018c2ecf20Sopenharmony_ciSMM665_ATTR(in3, input, SMM665_MISC16_ADC_DATA_A); 4028c2ecf20Sopenharmony_ciSMM665_ATTR(in4, input, SMM665_MISC16_ADC_DATA_B); 4038c2ecf20Sopenharmony_ciSMM665_ATTR(in5, input, SMM665_MISC16_ADC_DATA_C); 4048c2ecf20Sopenharmony_ciSMM665_ATTR(in6, input, SMM665_MISC16_ADC_DATA_D); 4058c2ecf20Sopenharmony_ciSMM665_ATTR(in7, input, SMM665_MISC16_ADC_DATA_E); 4068c2ecf20Sopenharmony_ciSMM665_ATTR(in8, input, SMM665_MISC16_ADC_DATA_F); 4078c2ecf20Sopenharmony_ciSMM665_ATTR(in9, input, SMM665_MISC16_ADC_DATA_AIN1); 4088c2ecf20Sopenharmony_ciSMM665_ATTR(in10, input, SMM665_MISC16_ADC_DATA_AIN2); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/* Input voltages min */ 4118c2ecf20Sopenharmony_ciSMM665_ATTR(in1, min, SMM665_MISC16_ADC_DATA_12V); 4128c2ecf20Sopenharmony_ciSMM665_ATTR(in2, min, SMM665_MISC16_ADC_DATA_VDD); 4138c2ecf20Sopenharmony_ciSMM665_ATTR(in3, min, SMM665_MISC16_ADC_DATA_A); 4148c2ecf20Sopenharmony_ciSMM665_ATTR(in4, min, SMM665_MISC16_ADC_DATA_B); 4158c2ecf20Sopenharmony_ciSMM665_ATTR(in5, min, SMM665_MISC16_ADC_DATA_C); 4168c2ecf20Sopenharmony_ciSMM665_ATTR(in6, min, SMM665_MISC16_ADC_DATA_D); 4178c2ecf20Sopenharmony_ciSMM665_ATTR(in7, min, SMM665_MISC16_ADC_DATA_E); 4188c2ecf20Sopenharmony_ciSMM665_ATTR(in8, min, SMM665_MISC16_ADC_DATA_F); 4198c2ecf20Sopenharmony_ciSMM665_ATTR(in9, min, SMM665_MISC16_ADC_DATA_AIN1); 4208c2ecf20Sopenharmony_ciSMM665_ATTR(in10, min, SMM665_MISC16_ADC_DATA_AIN2); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/* Input voltages max */ 4238c2ecf20Sopenharmony_ciSMM665_ATTR(in1, max, SMM665_MISC16_ADC_DATA_12V); 4248c2ecf20Sopenharmony_ciSMM665_ATTR(in2, max, SMM665_MISC16_ADC_DATA_VDD); 4258c2ecf20Sopenharmony_ciSMM665_ATTR(in3, max, SMM665_MISC16_ADC_DATA_A); 4268c2ecf20Sopenharmony_ciSMM665_ATTR(in4, max, SMM665_MISC16_ADC_DATA_B); 4278c2ecf20Sopenharmony_ciSMM665_ATTR(in5, max, SMM665_MISC16_ADC_DATA_C); 4288c2ecf20Sopenharmony_ciSMM665_ATTR(in6, max, SMM665_MISC16_ADC_DATA_D); 4298c2ecf20Sopenharmony_ciSMM665_ATTR(in7, max, SMM665_MISC16_ADC_DATA_E); 4308c2ecf20Sopenharmony_ciSMM665_ATTR(in8, max, SMM665_MISC16_ADC_DATA_F); 4318c2ecf20Sopenharmony_ciSMM665_ATTR(in9, max, SMM665_MISC16_ADC_DATA_AIN1); 4328c2ecf20Sopenharmony_ciSMM665_ATTR(in10, max, SMM665_MISC16_ADC_DATA_AIN2); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci/* Input voltages lcrit */ 4358c2ecf20Sopenharmony_ciSMM665_ATTR(in1, lcrit, SMM665_MISC16_ADC_DATA_12V); 4368c2ecf20Sopenharmony_ciSMM665_ATTR(in2, lcrit, SMM665_MISC16_ADC_DATA_VDD); 4378c2ecf20Sopenharmony_ciSMM665_ATTR(in3, lcrit, SMM665_MISC16_ADC_DATA_A); 4388c2ecf20Sopenharmony_ciSMM665_ATTR(in4, lcrit, SMM665_MISC16_ADC_DATA_B); 4398c2ecf20Sopenharmony_ciSMM665_ATTR(in5, lcrit, SMM665_MISC16_ADC_DATA_C); 4408c2ecf20Sopenharmony_ciSMM665_ATTR(in6, lcrit, SMM665_MISC16_ADC_DATA_D); 4418c2ecf20Sopenharmony_ciSMM665_ATTR(in7, lcrit, SMM665_MISC16_ADC_DATA_E); 4428c2ecf20Sopenharmony_ciSMM665_ATTR(in8, lcrit, SMM665_MISC16_ADC_DATA_F); 4438c2ecf20Sopenharmony_ciSMM665_ATTR(in9, lcrit, SMM665_MISC16_ADC_DATA_AIN1); 4448c2ecf20Sopenharmony_ciSMM665_ATTR(in10, lcrit, SMM665_MISC16_ADC_DATA_AIN2); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/* Input voltages crit */ 4478c2ecf20Sopenharmony_ciSMM665_ATTR(in1, crit, SMM665_MISC16_ADC_DATA_12V); 4488c2ecf20Sopenharmony_ciSMM665_ATTR(in2, crit, SMM665_MISC16_ADC_DATA_VDD); 4498c2ecf20Sopenharmony_ciSMM665_ATTR(in3, crit, SMM665_MISC16_ADC_DATA_A); 4508c2ecf20Sopenharmony_ciSMM665_ATTR(in4, crit, SMM665_MISC16_ADC_DATA_B); 4518c2ecf20Sopenharmony_ciSMM665_ATTR(in5, crit, SMM665_MISC16_ADC_DATA_C); 4528c2ecf20Sopenharmony_ciSMM665_ATTR(in6, crit, SMM665_MISC16_ADC_DATA_D); 4538c2ecf20Sopenharmony_ciSMM665_ATTR(in7, crit, SMM665_MISC16_ADC_DATA_E); 4548c2ecf20Sopenharmony_ciSMM665_ATTR(in8, crit, SMM665_MISC16_ADC_DATA_F); 4558c2ecf20Sopenharmony_ciSMM665_ATTR(in9, crit, SMM665_MISC16_ADC_DATA_AIN1); 4568c2ecf20Sopenharmony_ciSMM665_ATTR(in10, crit, SMM665_MISC16_ADC_DATA_AIN2); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/* critical alarms */ 4598c2ecf20Sopenharmony_ciSMM665_ATTR(in1, crit_alarm, SMM665_FAULT_12V); 4608c2ecf20Sopenharmony_ciSMM665_ATTR(in2, crit_alarm, SMM665_FAULT_VDD); 4618c2ecf20Sopenharmony_ciSMM665_ATTR(in3, crit_alarm, SMM665_FAULT_A); 4628c2ecf20Sopenharmony_ciSMM665_ATTR(in4, crit_alarm, SMM665_FAULT_B); 4638c2ecf20Sopenharmony_ciSMM665_ATTR(in5, crit_alarm, SMM665_FAULT_C); 4648c2ecf20Sopenharmony_ciSMM665_ATTR(in6, crit_alarm, SMM665_FAULT_D); 4658c2ecf20Sopenharmony_ciSMM665_ATTR(in7, crit_alarm, SMM665_FAULT_E); 4668c2ecf20Sopenharmony_ciSMM665_ATTR(in8, crit_alarm, SMM665_FAULT_F); 4678c2ecf20Sopenharmony_ciSMM665_ATTR(in9, crit_alarm, SMM665_FAULT_AIN1); 4688c2ecf20Sopenharmony_ciSMM665_ATTR(in10, crit_alarm, SMM665_FAULT_AIN2); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/* Temperature */ 4718c2ecf20Sopenharmony_ciSMM665_ATTR(temp1, input, SMM665_MISC16_ADC_DATA_INT_TEMP); 4728c2ecf20Sopenharmony_ciSMM665_ATTR(temp1, min, SMM665_MISC16_ADC_DATA_INT_TEMP); 4738c2ecf20Sopenharmony_ciSMM665_ATTR(temp1, max, SMM665_MISC16_ADC_DATA_INT_TEMP); 4748c2ecf20Sopenharmony_ciSMM665_ATTR(temp1, lcrit, SMM665_MISC16_ADC_DATA_INT_TEMP); 4758c2ecf20Sopenharmony_ciSMM665_ATTR(temp1, crit, SMM665_MISC16_ADC_DATA_INT_TEMP); 4768c2ecf20Sopenharmony_ciSMM665_ATTR(temp1, crit_alarm, SMM665_FAULT_TEMP); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/* 4798c2ecf20Sopenharmony_ci * Finally, construct an array of pointers to members of the above objects, 4808c2ecf20Sopenharmony_ci * as required for sysfs_create_group() 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_cistatic struct attribute *smm665_attrs[] = { 4838c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_input.dev_attr.attr, 4848c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_min.dev_attr.attr, 4858c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_max.dev_attr.attr, 4868c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_lcrit.dev_attr.attr, 4878c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_crit.dev_attr.attr, 4888c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_crit_alarm.dev_attr.attr, 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_input.dev_attr.attr, 4918c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_min.dev_attr.attr, 4928c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_max.dev_attr.attr, 4938c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_lcrit.dev_attr.attr, 4948c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_crit.dev_attr.attr, 4958c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_crit_alarm.dev_attr.attr, 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_input.dev_attr.attr, 4988c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_min.dev_attr.attr, 4998c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_max.dev_attr.attr, 5008c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_lcrit.dev_attr.attr, 5018c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_crit.dev_attr.attr, 5028c2ecf20Sopenharmony_ci &sensor_dev_attr_in3_crit_alarm.dev_attr.attr, 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_input.dev_attr.attr, 5058c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_min.dev_attr.attr, 5068c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_max.dev_attr.attr, 5078c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_lcrit.dev_attr.attr, 5088c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_crit.dev_attr.attr, 5098c2ecf20Sopenharmony_ci &sensor_dev_attr_in4_crit_alarm.dev_attr.attr, 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_input.dev_attr.attr, 5128c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_min.dev_attr.attr, 5138c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_max.dev_attr.attr, 5148c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_lcrit.dev_attr.attr, 5158c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_crit.dev_attr.attr, 5168c2ecf20Sopenharmony_ci &sensor_dev_attr_in5_crit_alarm.dev_attr.attr, 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci &sensor_dev_attr_in6_input.dev_attr.attr, 5198c2ecf20Sopenharmony_ci &sensor_dev_attr_in6_min.dev_attr.attr, 5208c2ecf20Sopenharmony_ci &sensor_dev_attr_in6_max.dev_attr.attr, 5218c2ecf20Sopenharmony_ci &sensor_dev_attr_in6_lcrit.dev_attr.attr, 5228c2ecf20Sopenharmony_ci &sensor_dev_attr_in6_crit.dev_attr.attr, 5238c2ecf20Sopenharmony_ci &sensor_dev_attr_in6_crit_alarm.dev_attr.attr, 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci &sensor_dev_attr_in7_input.dev_attr.attr, 5268c2ecf20Sopenharmony_ci &sensor_dev_attr_in7_min.dev_attr.attr, 5278c2ecf20Sopenharmony_ci &sensor_dev_attr_in7_max.dev_attr.attr, 5288c2ecf20Sopenharmony_ci &sensor_dev_attr_in7_lcrit.dev_attr.attr, 5298c2ecf20Sopenharmony_ci &sensor_dev_attr_in7_crit.dev_attr.attr, 5308c2ecf20Sopenharmony_ci &sensor_dev_attr_in7_crit_alarm.dev_attr.attr, 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci &sensor_dev_attr_in8_input.dev_attr.attr, 5338c2ecf20Sopenharmony_ci &sensor_dev_attr_in8_min.dev_attr.attr, 5348c2ecf20Sopenharmony_ci &sensor_dev_attr_in8_max.dev_attr.attr, 5358c2ecf20Sopenharmony_ci &sensor_dev_attr_in8_lcrit.dev_attr.attr, 5368c2ecf20Sopenharmony_ci &sensor_dev_attr_in8_crit.dev_attr.attr, 5378c2ecf20Sopenharmony_ci &sensor_dev_attr_in8_crit_alarm.dev_attr.attr, 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci &sensor_dev_attr_in9_input.dev_attr.attr, 5408c2ecf20Sopenharmony_ci &sensor_dev_attr_in9_min.dev_attr.attr, 5418c2ecf20Sopenharmony_ci &sensor_dev_attr_in9_max.dev_attr.attr, 5428c2ecf20Sopenharmony_ci &sensor_dev_attr_in9_lcrit.dev_attr.attr, 5438c2ecf20Sopenharmony_ci &sensor_dev_attr_in9_crit.dev_attr.attr, 5448c2ecf20Sopenharmony_ci &sensor_dev_attr_in9_crit_alarm.dev_attr.attr, 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci &sensor_dev_attr_in10_input.dev_attr.attr, 5478c2ecf20Sopenharmony_ci &sensor_dev_attr_in10_min.dev_attr.attr, 5488c2ecf20Sopenharmony_ci &sensor_dev_attr_in10_max.dev_attr.attr, 5498c2ecf20Sopenharmony_ci &sensor_dev_attr_in10_lcrit.dev_attr.attr, 5508c2ecf20Sopenharmony_ci &sensor_dev_attr_in10_crit.dev_attr.attr, 5518c2ecf20Sopenharmony_ci &sensor_dev_attr_in10_crit_alarm.dev_attr.attr, 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 5548c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_min.dev_attr.attr, 5558c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max.dev_attr.attr, 5568c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_lcrit.dev_attr.attr, 5578c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit.dev_attr.attr, 5588c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci NULL, 5618c2ecf20Sopenharmony_ci}; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(smm665); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic const struct i2c_device_id smm665_id[]; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic int smm665_probe(struct i2c_client *client) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 5708c2ecf20Sopenharmony_ci struct smm665_data *data; 5718c2ecf20Sopenharmony_ci struct device *hwmon_dev; 5728c2ecf20Sopenharmony_ci int i, ret; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA 5758c2ecf20Sopenharmony_ci | I2C_FUNC_SMBUS_WORD_DATA)) 5768c2ecf20Sopenharmony_ci return -ENODEV; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (i2c_smbus_read_byte_data(client, SMM665_ADOC_ENABLE) < 0) 5798c2ecf20Sopenharmony_ci return -ENODEV; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 5828c2ecf20Sopenharmony_ci if (!data) 5838c2ecf20Sopenharmony_ci return -ENOMEM; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci i2c_set_clientdata(client, data); 5868c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci data->client = client; 5898c2ecf20Sopenharmony_ci data->type = i2c_match_id(smm665_id, client)->driver_data; 5908c2ecf20Sopenharmony_ci data->cmdreg = i2c_new_dummy_device(adapter, (client->addr & ~SMM665_REGMASK) 5918c2ecf20Sopenharmony_ci | SMM665_CMDREG_BASE); 5928c2ecf20Sopenharmony_ci if (IS_ERR(data->cmdreg)) 5938c2ecf20Sopenharmony_ci return PTR_ERR(data->cmdreg); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci switch (data->type) { 5968c2ecf20Sopenharmony_ci case smm465: 5978c2ecf20Sopenharmony_ci case smm665: 5988c2ecf20Sopenharmony_ci data->conversion_time = SMM665_ADC_WAIT_SMM665; 5998c2ecf20Sopenharmony_ci break; 6008c2ecf20Sopenharmony_ci case smm665c: 6018c2ecf20Sopenharmony_ci case smm764: 6028c2ecf20Sopenharmony_ci case smm766: 6038c2ecf20Sopenharmony_ci data->conversion_time = SMM665_ADC_WAIT_SMM766; 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci ret = -ENODEV; 6088c2ecf20Sopenharmony_ci if (i2c_smbus_read_byte_data(data->cmdreg, SMM665_MISC8_CMD_STS) < 0) 6098c2ecf20Sopenharmony_ci goto out_unregister; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* 6128c2ecf20Sopenharmony_ci * Read limits. 6138c2ecf20Sopenharmony_ci * 6148c2ecf20Sopenharmony_ci * Limit registers start with register SMM665_LIMIT_BASE. 6158c2ecf20Sopenharmony_ci * Each channel uses 8 registers, providing four limit values 6168c2ecf20Sopenharmony_ci * per channel. Each limit value requires two registers, with the 6178c2ecf20Sopenharmony_ci * high byte in the first register and the low byte in the second 6188c2ecf20Sopenharmony_ci * register. The first two limits are under limit values, followed 6198c2ecf20Sopenharmony_ci * by two over limit values. 6208c2ecf20Sopenharmony_ci * 6218c2ecf20Sopenharmony_ci * Limit register order matches the ADC register order, so we use 6228c2ecf20Sopenharmony_ci * ADC register defines throughout the code to index limit registers. 6238c2ecf20Sopenharmony_ci * 6248c2ecf20Sopenharmony_ci * We save the first retrieved value both as "critical" and "alarm" 6258c2ecf20Sopenharmony_ci * value. The second value overwrites either the critical or the 6268c2ecf20Sopenharmony_ci * alarm value, depending on its configuration. This ensures that both 6278c2ecf20Sopenharmony_ci * critical and alarm values are initialized, even if both registers are 6288c2ecf20Sopenharmony_ci * configured as critical or non-critical. 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_ci for (i = 0; i < SMM665_NUM_ADC; i++) { 6318c2ecf20Sopenharmony_ci int val; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8); 6348c2ecf20Sopenharmony_ci if (unlikely(val < 0)) 6358c2ecf20Sopenharmony_ci goto out_unregister; 6368c2ecf20Sopenharmony_ci data->critical_min_limit[i] = data->alarm_min_limit[i] 6378c2ecf20Sopenharmony_ci = smm665_convert(val, i); 6388c2ecf20Sopenharmony_ci val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 2); 6398c2ecf20Sopenharmony_ci if (unlikely(val < 0)) 6408c2ecf20Sopenharmony_ci goto out_unregister; 6418c2ecf20Sopenharmony_ci if (smm665_is_critical(val)) 6428c2ecf20Sopenharmony_ci data->critical_min_limit[i] = smm665_convert(val, i); 6438c2ecf20Sopenharmony_ci else 6448c2ecf20Sopenharmony_ci data->alarm_min_limit[i] = smm665_convert(val, i); 6458c2ecf20Sopenharmony_ci val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 4); 6468c2ecf20Sopenharmony_ci if (unlikely(val < 0)) 6478c2ecf20Sopenharmony_ci goto out_unregister; 6488c2ecf20Sopenharmony_ci data->critical_max_limit[i] = data->alarm_max_limit[i] 6498c2ecf20Sopenharmony_ci = smm665_convert(val, i); 6508c2ecf20Sopenharmony_ci val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 6); 6518c2ecf20Sopenharmony_ci if (unlikely(val < 0)) 6528c2ecf20Sopenharmony_ci goto out_unregister; 6538c2ecf20Sopenharmony_ci if (smm665_is_critical(val)) 6548c2ecf20Sopenharmony_ci data->critical_max_limit[i] = smm665_convert(val, i); 6558c2ecf20Sopenharmony_ci else 6568c2ecf20Sopenharmony_ci data->alarm_max_limit[i] = smm665_convert(val, i); 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, 6608c2ecf20Sopenharmony_ci client->name, data, 6618c2ecf20Sopenharmony_ci smm665_groups); 6628c2ecf20Sopenharmony_ci if (IS_ERR(hwmon_dev)) { 6638c2ecf20Sopenharmony_ci ret = PTR_ERR(hwmon_dev); 6648c2ecf20Sopenharmony_ci goto out_unregister; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci return 0; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ciout_unregister: 6708c2ecf20Sopenharmony_ci i2c_unregister_device(data->cmdreg); 6718c2ecf20Sopenharmony_ci return ret; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int smm665_remove(struct i2c_client *client) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct smm665_data *data = i2c_get_clientdata(client); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci i2c_unregister_device(data->cmdreg); 6798c2ecf20Sopenharmony_ci return 0; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic const struct i2c_device_id smm665_id[] = { 6838c2ecf20Sopenharmony_ci {"smm465", smm465}, 6848c2ecf20Sopenharmony_ci {"smm665", smm665}, 6858c2ecf20Sopenharmony_ci {"smm665c", smm665c}, 6868c2ecf20Sopenharmony_ci {"smm764", smm764}, 6878c2ecf20Sopenharmony_ci {"smm766", smm766}, 6888c2ecf20Sopenharmony_ci {} 6898c2ecf20Sopenharmony_ci}; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, smm665_id); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci/* This is the driver that will be inserted */ 6948c2ecf20Sopenharmony_cistatic struct i2c_driver smm665_driver = { 6958c2ecf20Sopenharmony_ci .driver = { 6968c2ecf20Sopenharmony_ci .name = "smm665", 6978c2ecf20Sopenharmony_ci }, 6988c2ecf20Sopenharmony_ci .probe_new = smm665_probe, 6998c2ecf20Sopenharmony_ci .remove = smm665_remove, 7008c2ecf20Sopenharmony_ci .id_table = smm665_id, 7018c2ecf20Sopenharmony_ci}; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cimodule_i2c_driver(smm665_driver); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guenter Roeck"); 7068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SMM665 driver"); 7078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 708