162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * thmc50.c - Part of lm_sensors, Linux kernel modules for hardware 462306a36Sopenharmony_ci * monitoring 562306a36Sopenharmony_ci * Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl> 662306a36Sopenharmony_ci * Based on 2.4 driver by Frodo Looijaard <frodol@dds.nl> and 762306a36Sopenharmony_ci * Philip Edelbrock <phil@netroedge.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/i2c.h> 1462306a36Sopenharmony_ci#include <linux/hwmon.h> 1562306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/jiffies.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Addresses to scan */ 2362306a36Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Insmod parameters */ 2662306a36Sopenharmony_cienum chips { thmc50, adm1022 }; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic unsigned short adm1022_temp3[16]; 2962306a36Sopenharmony_cistatic unsigned int adm1022_temp3_num; 3062306a36Sopenharmony_cimodule_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0); 3162306a36Sopenharmony_ciMODULE_PARM_DESC(adm1022_temp3, 3262306a36Sopenharmony_ci "List of adapter,address pairs to enable 3rd temperature (ADM1022 only)"); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Many THMC50 constants specified below */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* The THMC50 registers */ 3762306a36Sopenharmony_ci#define THMC50_REG_CONF 0x40 3862306a36Sopenharmony_ci#define THMC50_REG_COMPANY_ID 0x3E 3962306a36Sopenharmony_ci#define THMC50_REG_DIE_CODE 0x3F 4062306a36Sopenharmony_ci#define THMC50_REG_ANALOG_OUT 0x19 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * The mirror status register cannot be used as 4362306a36Sopenharmony_ci * reading it does not clear alarms. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci#define THMC50_REG_INTR 0x41 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic const u8 THMC50_REG_TEMP[] = { 0x27, 0x26, 0x20 }; 4862306a36Sopenharmony_cistatic const u8 THMC50_REG_TEMP_MIN[] = { 0x3A, 0x38, 0x2C }; 4962306a36Sopenharmony_cistatic const u8 THMC50_REG_TEMP_MAX[] = { 0x39, 0x37, 0x2B }; 5062306a36Sopenharmony_cistatic const u8 THMC50_REG_TEMP_CRITICAL[] = { 0x13, 0x14, 0x14 }; 5162306a36Sopenharmony_cistatic const u8 THMC50_REG_TEMP_DEFAULT[] = { 0x17, 0x18, 0x18 }; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define THMC50_REG_CONF_nFANOFF 0x20 5462306a36Sopenharmony_ci#define THMC50_REG_CONF_PROGRAMMED 0x08 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Each client has this additional data */ 5762306a36Sopenharmony_cistruct thmc50_data { 5862306a36Sopenharmony_ci struct i2c_client *client; 5962306a36Sopenharmony_ci const struct attribute_group *groups[3]; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci struct mutex update_lock; 6262306a36Sopenharmony_ci enum chips type; 6362306a36Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 6462306a36Sopenharmony_ci char has_temp3; /* !=0 if it is ADM1022 in temp3 mode */ 6562306a36Sopenharmony_ci bool valid; /* true if following fields are valid */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Register values */ 6862306a36Sopenharmony_ci s8 temp_input[3]; 6962306a36Sopenharmony_ci s8 temp_max[3]; 7062306a36Sopenharmony_ci s8 temp_min[3]; 7162306a36Sopenharmony_ci s8 temp_critical[3]; 7262306a36Sopenharmony_ci u8 analog_out; 7362306a36Sopenharmony_ci u8 alarms; 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct thmc50_data *thmc50_update_device(struct device *dev) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct thmc50_data *data = dev_get_drvdata(dev); 7962306a36Sopenharmony_ci struct i2c_client *client = data->client; 8062306a36Sopenharmony_ci int timeout = HZ / 5 + (data->type == thmc50 ? HZ : 0); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci mutex_lock(&data->update_lock); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (time_after(jiffies, data->last_updated + timeout) 8562306a36Sopenharmony_ci || !data->valid) { 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci int temps = data->has_temp3 ? 3 : 2; 8862306a36Sopenharmony_ci int i; 8962306a36Sopenharmony_ci int prog = i2c_smbus_read_byte_data(client, THMC50_REG_CONF); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci prog &= THMC50_REG_CONF_PROGRAMMED; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci for (i = 0; i < temps; i++) { 9462306a36Sopenharmony_ci data->temp_input[i] = i2c_smbus_read_byte_data(client, 9562306a36Sopenharmony_ci THMC50_REG_TEMP[i]); 9662306a36Sopenharmony_ci data->temp_max[i] = i2c_smbus_read_byte_data(client, 9762306a36Sopenharmony_ci THMC50_REG_TEMP_MAX[i]); 9862306a36Sopenharmony_ci data->temp_min[i] = i2c_smbus_read_byte_data(client, 9962306a36Sopenharmony_ci THMC50_REG_TEMP_MIN[i]); 10062306a36Sopenharmony_ci data->temp_critical[i] = 10162306a36Sopenharmony_ci i2c_smbus_read_byte_data(client, 10262306a36Sopenharmony_ci prog ? THMC50_REG_TEMP_CRITICAL[i] 10362306a36Sopenharmony_ci : THMC50_REG_TEMP_DEFAULT[i]); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci data->analog_out = 10662306a36Sopenharmony_ci i2c_smbus_read_byte_data(client, THMC50_REG_ANALOG_OUT); 10762306a36Sopenharmony_ci data->alarms = 10862306a36Sopenharmony_ci i2c_smbus_read_byte_data(client, THMC50_REG_INTR); 10962306a36Sopenharmony_ci data->last_updated = jiffies; 11062306a36Sopenharmony_ci data->valid = true; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return data; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic ssize_t analog_out_show(struct device *dev, 11962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct thmc50_data *data = thmc50_update_device(dev); 12262306a36Sopenharmony_ci return sprintf(buf, "%d\n", data->analog_out); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic ssize_t analog_out_store(struct device *dev, 12662306a36Sopenharmony_ci struct device_attribute *attr, 12762306a36Sopenharmony_ci const char *buf, size_t count) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct thmc50_data *data = dev_get_drvdata(dev); 13062306a36Sopenharmony_ci struct i2c_client *client = data->client; 13162306a36Sopenharmony_ci int config; 13262306a36Sopenharmony_ci unsigned long tmp; 13362306a36Sopenharmony_ci int err; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci err = kstrtoul(buf, 10, &tmp); 13662306a36Sopenharmony_ci if (err) 13762306a36Sopenharmony_ci return err; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci mutex_lock(&data->update_lock); 14062306a36Sopenharmony_ci data->analog_out = clamp_val(tmp, 0, 255); 14162306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, THMC50_REG_ANALOG_OUT, 14262306a36Sopenharmony_ci data->analog_out); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF); 14562306a36Sopenharmony_ci if (data->analog_out == 0) 14662306a36Sopenharmony_ci config &= ~THMC50_REG_CONF_nFANOFF; 14762306a36Sopenharmony_ci else 14862306a36Sopenharmony_ci config |= THMC50_REG_CONF_nFANOFF; 14962306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 15262306a36Sopenharmony_ci return count; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* There is only one PWM mode = DC */ 15662306a36Sopenharmony_cistatic ssize_t pwm_mode_show(struct device *dev, 15762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci return sprintf(buf, "0\n"); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* Temperatures */ 16362306a36Sopenharmony_cistatic ssize_t temp_show(struct device *dev, struct device_attribute *attr, 16462306a36Sopenharmony_ci char *buf) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 16762306a36Sopenharmony_ci struct thmc50_data *data = thmc50_update_device(dev); 16862306a36Sopenharmony_ci return sprintf(buf, "%d\n", data->temp_input[nr] * 1000); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic ssize_t temp_min_show(struct device *dev, 17262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 17562306a36Sopenharmony_ci struct thmc50_data *data = thmc50_update_device(dev); 17662306a36Sopenharmony_ci return sprintf(buf, "%d\n", data->temp_min[nr] * 1000); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic ssize_t temp_min_store(struct device *dev, 18062306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, 18162306a36Sopenharmony_ci size_t count) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 18462306a36Sopenharmony_ci struct thmc50_data *data = dev_get_drvdata(dev); 18562306a36Sopenharmony_ci struct i2c_client *client = data->client; 18662306a36Sopenharmony_ci long val; 18762306a36Sopenharmony_ci int err; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 19062306a36Sopenharmony_ci if (err) 19162306a36Sopenharmony_ci return err; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci mutex_lock(&data->update_lock); 19462306a36Sopenharmony_ci data->temp_min[nr] = clamp_val(val / 1000, -128, 127); 19562306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, THMC50_REG_TEMP_MIN[nr], 19662306a36Sopenharmony_ci data->temp_min[nr]); 19762306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 19862306a36Sopenharmony_ci return count; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic ssize_t temp_max_show(struct device *dev, 20262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 20562306a36Sopenharmony_ci struct thmc50_data *data = thmc50_update_device(dev); 20662306a36Sopenharmony_ci return sprintf(buf, "%d\n", data->temp_max[nr] * 1000); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic ssize_t temp_max_store(struct device *dev, 21062306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, 21162306a36Sopenharmony_ci size_t count) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 21462306a36Sopenharmony_ci struct thmc50_data *data = dev_get_drvdata(dev); 21562306a36Sopenharmony_ci struct i2c_client *client = data->client; 21662306a36Sopenharmony_ci long val; 21762306a36Sopenharmony_ci int err; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 22062306a36Sopenharmony_ci if (err) 22162306a36Sopenharmony_ci return err; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci mutex_lock(&data->update_lock); 22462306a36Sopenharmony_ci data->temp_max[nr] = clamp_val(val / 1000, -128, 127); 22562306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, THMC50_REG_TEMP_MAX[nr], 22662306a36Sopenharmony_ci data->temp_max[nr]); 22762306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 22862306a36Sopenharmony_ci return count; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic ssize_t temp_critical_show(struct device *dev, 23262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci int nr = to_sensor_dev_attr(attr)->index; 23562306a36Sopenharmony_ci struct thmc50_data *data = thmc50_update_device(dev); 23662306a36Sopenharmony_ci return sprintf(buf, "%d\n", data->temp_critical[nr] * 1000); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic ssize_t alarm_show(struct device *dev, struct device_attribute *attr, 24062306a36Sopenharmony_ci char *buf) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci int index = to_sensor_dev_attr(attr)->index; 24362306a36Sopenharmony_ci struct thmc50_data *data = thmc50_update_device(dev); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return sprintf(buf, "%u\n", (data->alarms >> index) & 1); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); 24962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0); 25062306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0); 25162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_crit, temp_critical, 0); 25262306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); 25362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1); 25462306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1); 25562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_crit, temp_critical, 1); 25662306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); 25762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2); 25862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2); 25962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_crit, temp_critical, 2); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 0); 26262306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 5); 26362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 1); 26462306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 7); 26562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 2); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm1, analog_out, 0); 26862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(pwm1_mode, pwm_mode, 0); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic struct attribute *thmc50_attributes[] = { 27162306a36Sopenharmony_ci &sensor_dev_attr_temp1_max.dev_attr.attr, 27262306a36Sopenharmony_ci &sensor_dev_attr_temp1_min.dev_attr.attr, 27362306a36Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 27462306a36Sopenharmony_ci &sensor_dev_attr_temp1_crit.dev_attr.attr, 27562306a36Sopenharmony_ci &sensor_dev_attr_temp1_alarm.dev_attr.attr, 27662306a36Sopenharmony_ci &sensor_dev_attr_temp2_max.dev_attr.attr, 27762306a36Sopenharmony_ci &sensor_dev_attr_temp2_min.dev_attr.attr, 27862306a36Sopenharmony_ci &sensor_dev_attr_temp2_input.dev_attr.attr, 27962306a36Sopenharmony_ci &sensor_dev_attr_temp2_crit.dev_attr.attr, 28062306a36Sopenharmony_ci &sensor_dev_attr_temp2_alarm.dev_attr.attr, 28162306a36Sopenharmony_ci &sensor_dev_attr_temp2_fault.dev_attr.attr, 28262306a36Sopenharmony_ci &sensor_dev_attr_pwm1.dev_attr.attr, 28362306a36Sopenharmony_ci &sensor_dev_attr_pwm1_mode.dev_attr.attr, 28462306a36Sopenharmony_ci NULL 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic const struct attribute_group thmc50_group = { 28862306a36Sopenharmony_ci .attrs = thmc50_attributes, 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/* for ADM1022 3rd temperature mode */ 29262306a36Sopenharmony_cistatic struct attribute *temp3_attributes[] = { 29362306a36Sopenharmony_ci &sensor_dev_attr_temp3_max.dev_attr.attr, 29462306a36Sopenharmony_ci &sensor_dev_attr_temp3_min.dev_attr.attr, 29562306a36Sopenharmony_ci &sensor_dev_attr_temp3_input.dev_attr.attr, 29662306a36Sopenharmony_ci &sensor_dev_attr_temp3_crit.dev_attr.attr, 29762306a36Sopenharmony_ci &sensor_dev_attr_temp3_alarm.dev_attr.attr, 29862306a36Sopenharmony_ci &sensor_dev_attr_temp3_fault.dev_attr.attr, 29962306a36Sopenharmony_ci NULL 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic const struct attribute_group temp3_group = { 30362306a36Sopenharmony_ci .attrs = temp3_attributes, 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */ 30762306a36Sopenharmony_cistatic int thmc50_detect(struct i2c_client *client, 30862306a36Sopenharmony_ci struct i2c_board_info *info) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci unsigned company; 31162306a36Sopenharmony_ci unsigned revision; 31262306a36Sopenharmony_ci unsigned config; 31362306a36Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 31462306a36Sopenharmony_ci const char *type_name; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { 31762306a36Sopenharmony_ci pr_debug("thmc50: detect failed, smbus byte data not supported!\n"); 31862306a36Sopenharmony_ci return -ENODEV; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci pr_debug("thmc50: Probing for THMC50 at 0x%2X on bus %d\n", 32262306a36Sopenharmony_ci client->addr, i2c_adapter_id(client->adapter)); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci company = i2c_smbus_read_byte_data(client, THMC50_REG_COMPANY_ID); 32562306a36Sopenharmony_ci revision = i2c_smbus_read_byte_data(client, THMC50_REG_DIE_CODE); 32662306a36Sopenharmony_ci config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF); 32762306a36Sopenharmony_ci if (revision < 0xc0 || (config & 0x10)) 32862306a36Sopenharmony_ci return -ENODEV; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (company == 0x41) { 33162306a36Sopenharmony_ci int id = i2c_adapter_id(client->adapter); 33262306a36Sopenharmony_ci int i; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci type_name = "adm1022"; 33562306a36Sopenharmony_ci for (i = 0; i + 1 < adm1022_temp3_num; i += 2) 33662306a36Sopenharmony_ci if (adm1022_temp3[i] == id && 33762306a36Sopenharmony_ci adm1022_temp3[i + 1] == client->addr) { 33862306a36Sopenharmony_ci /* enable 2nd remote temp */ 33962306a36Sopenharmony_ci config |= (1 << 7); 34062306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, 34162306a36Sopenharmony_ci THMC50_REG_CONF, 34262306a36Sopenharmony_ci config); 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci } else if (company == 0x49) { 34662306a36Sopenharmony_ci type_name = "thmc50"; 34762306a36Sopenharmony_ci } else { 34862306a36Sopenharmony_ci pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n"); 34962306a36Sopenharmony_ci return -ENODEV; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci pr_debug("thmc50: Detected %s (version %x, revision %x)\n", 35362306a36Sopenharmony_ci type_name, (revision >> 4) - 0xc, revision & 0xf); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci strscpy(info->type, type_name, I2C_NAME_SIZE); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void thmc50_init_client(struct thmc50_data *data) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct i2c_client *client = data->client; 36362306a36Sopenharmony_ci int config; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci data->analog_out = i2c_smbus_read_byte_data(client, 36662306a36Sopenharmony_ci THMC50_REG_ANALOG_OUT); 36762306a36Sopenharmony_ci /* set up to at least 1 */ 36862306a36Sopenharmony_ci if (data->analog_out == 0) { 36962306a36Sopenharmony_ci data->analog_out = 1; 37062306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, THMC50_REG_ANALOG_OUT, 37162306a36Sopenharmony_ci data->analog_out); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF); 37462306a36Sopenharmony_ci config |= 0x1; /* start the chip if it is in standby mode */ 37562306a36Sopenharmony_ci if (data->type == adm1022 && (config & (1 << 7))) 37662306a36Sopenharmony_ci data->has_temp3 = 1; 37762306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic const struct i2c_device_id thmc50_id[]; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int thmc50_probe(struct i2c_client *client) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct device *dev = &client->dev; 38562306a36Sopenharmony_ci struct thmc50_data *data; 38662306a36Sopenharmony_ci struct device *hwmon_dev; 38762306a36Sopenharmony_ci int idx = 0; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct thmc50_data), GFP_KERNEL); 39062306a36Sopenharmony_ci if (!data) 39162306a36Sopenharmony_ci return -ENOMEM; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci data->client = client; 39462306a36Sopenharmony_ci data->type = i2c_match_id(thmc50_id, client)->driver_data; 39562306a36Sopenharmony_ci mutex_init(&data->update_lock); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci thmc50_init_client(data); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* sysfs hooks */ 40062306a36Sopenharmony_ci data->groups[idx++] = &thmc50_group; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Register additional ADM1022 sysfs hooks */ 40362306a36Sopenharmony_ci if (data->has_temp3) 40462306a36Sopenharmony_ci data->groups[idx++] = &temp3_group; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 40762306a36Sopenharmony_ci data, data->groups); 40862306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic const struct i2c_device_id thmc50_id[] = { 41262306a36Sopenharmony_ci { "adm1022", adm1022 }, 41362306a36Sopenharmony_ci { "thmc50", thmc50 }, 41462306a36Sopenharmony_ci { } 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, thmc50_id); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic struct i2c_driver thmc50_driver = { 41962306a36Sopenharmony_ci .class = I2C_CLASS_HWMON, 42062306a36Sopenharmony_ci .driver = { 42162306a36Sopenharmony_ci .name = "thmc50", 42262306a36Sopenharmony_ci }, 42362306a36Sopenharmony_ci .probe = thmc50_probe, 42462306a36Sopenharmony_ci .id_table = thmc50_id, 42562306a36Sopenharmony_ci .detect = thmc50_detect, 42662306a36Sopenharmony_ci .address_list = normal_i2c, 42762306a36Sopenharmony_ci}; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cimodule_i2c_driver(thmc50_driver); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ciMODULE_AUTHOR("Krzysztof Helt <krzysztof.h1@wp.pl>"); 43262306a36Sopenharmony_ciMODULE_DESCRIPTION("THMC50 driver"); 433