162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Texas Instruments TMP512, TMP513 power monitor chips 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * TMP513: 662306a36Sopenharmony_ci * Thermal/Power Management with Triple Remote and 762306a36Sopenharmony_ci * Local Temperature Sensor and Current Shunt Monitor 862306a36Sopenharmony_ci * Datasheet: https://www.ti.com/lit/gpn/tmp513 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * TMP512: 1162306a36Sopenharmony_ci * Thermal/Power Management with Dual Remote 1262306a36Sopenharmony_ci * and Local Temperature Sensor and Current Shunt Monitor 1362306a36Sopenharmony_ci * Datasheet: https://www.ti.com/lit/gpn/tmp512 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Copyright (C) 2019 Eric Tremblay <etremblay@distech-controls.com> 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 1862306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 1962306a36Sopenharmony_ci * the Free Software Foundation; version 2 of the License. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/err.h> 2362306a36Sopenharmony_ci#include <linux/hwmon.h> 2462306a36Sopenharmony_ci#include <linux/i2c.h> 2562306a36Sopenharmony_ci#include <linux/init.h> 2662306a36Sopenharmony_ci#include <linux/kernel.h> 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <linux/regmap.h> 2962306a36Sopenharmony_ci#include <linux/slab.h> 3062306a36Sopenharmony_ci#include <linux/util_macros.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci// Common register definition 3362306a36Sopenharmony_ci#define TMP51X_SHUNT_CONFIG 0x00 3462306a36Sopenharmony_ci#define TMP51X_TEMP_CONFIG 0x01 3562306a36Sopenharmony_ci#define TMP51X_STATUS 0x02 3662306a36Sopenharmony_ci#define TMP51X_SMBUS_ALERT 0x03 3762306a36Sopenharmony_ci#define TMP51X_SHUNT_CURRENT_RESULT 0x04 3862306a36Sopenharmony_ci#define TMP51X_BUS_VOLTAGE_RESULT 0x05 3962306a36Sopenharmony_ci#define TMP51X_POWER_RESULT 0x06 4062306a36Sopenharmony_ci#define TMP51X_BUS_CURRENT_RESULT 0x07 4162306a36Sopenharmony_ci#define TMP51X_LOCAL_TEMP_RESULT 0x08 4262306a36Sopenharmony_ci#define TMP51X_REMOTE_TEMP_RESULT_1 0x09 4362306a36Sopenharmony_ci#define TMP51X_REMOTE_TEMP_RESULT_2 0x0A 4462306a36Sopenharmony_ci#define TMP51X_SHUNT_CURRENT_H_LIMIT 0x0C 4562306a36Sopenharmony_ci#define TMP51X_SHUNT_CURRENT_L_LIMIT 0x0D 4662306a36Sopenharmony_ci#define TMP51X_BUS_VOLTAGE_H_LIMIT 0x0E 4762306a36Sopenharmony_ci#define TMP51X_BUS_VOLTAGE_L_LIMIT 0x0F 4862306a36Sopenharmony_ci#define TMP51X_POWER_LIMIT 0x10 4962306a36Sopenharmony_ci#define TMP51X_LOCAL_TEMP_LIMIT 0x11 5062306a36Sopenharmony_ci#define TMP51X_REMOTE_TEMP_LIMIT_1 0x12 5162306a36Sopenharmony_ci#define TMP51X_REMOTE_TEMP_LIMIT_2 0x13 5262306a36Sopenharmony_ci#define TMP51X_SHUNT_CALIBRATION 0x15 5362306a36Sopenharmony_ci#define TMP51X_N_FACTOR_AND_HYST_1 0x16 5462306a36Sopenharmony_ci#define TMP51X_N_FACTOR_2 0x17 5562306a36Sopenharmony_ci#define TMP51X_MAN_ID_REG 0xFE 5662306a36Sopenharmony_ci#define TMP51X_DEVICE_ID_REG 0xFF 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci// TMP513 specific register definition 5962306a36Sopenharmony_ci#define TMP513_REMOTE_TEMP_RESULT_3 0x0B 6062306a36Sopenharmony_ci#define TMP513_REMOTE_TEMP_LIMIT_3 0x14 6162306a36Sopenharmony_ci#define TMP513_N_FACTOR_3 0x18 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci// Common attrs, and NULL 6462306a36Sopenharmony_ci#define TMP51X_MANUFACTURER_ID 0x55FF 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define TMP512_DEVICE_ID 0x22FF 6762306a36Sopenharmony_ci#define TMP513_DEVICE_ID 0x23FF 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci// Default config 7062306a36Sopenharmony_ci#define TMP51X_SHUNT_CONFIG_DEFAULT 0x399F 7162306a36Sopenharmony_ci#define TMP51X_SHUNT_VALUE_DEFAULT 1000 7262306a36Sopenharmony_ci#define TMP51X_VBUS_RANGE_DEFAULT TMP51X_VBUS_RANGE_32V 7362306a36Sopenharmony_ci#define TMP51X_PGA_DEFAULT 8 7462306a36Sopenharmony_ci#define TMP51X_MAX_REGISTER_ADDR 0xFF 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define TMP512_TEMP_CONFIG_DEFAULT 0xBF80 7762306a36Sopenharmony_ci#define TMP513_TEMP_CONFIG_DEFAULT 0xFF80 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci// Mask and shift 8062306a36Sopenharmony_ci#define CURRENT_SENSE_VOLTAGE_320_MASK 0x1800 8162306a36Sopenharmony_ci#define CURRENT_SENSE_VOLTAGE_160_MASK 0x1000 8262306a36Sopenharmony_ci#define CURRENT_SENSE_VOLTAGE_80_MASK 0x0800 8362306a36Sopenharmony_ci#define CURRENT_SENSE_VOLTAGE_40_MASK 0 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define TMP51X_BUS_VOLTAGE_MASK 0x2000 8662306a36Sopenharmony_ci#define TMP51X_NFACTOR_MASK 0xFF00 8762306a36Sopenharmony_ci#define TMP51X_HYST_MASK 0x00FF 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define TMP51X_BUS_VOLTAGE_SHIFT 3 9062306a36Sopenharmony_ci#define TMP51X_TEMP_SHIFT 3 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci// Alarms 9362306a36Sopenharmony_ci#define TMP51X_SHUNT_CURRENT_H_LIMIT_POS 15 9462306a36Sopenharmony_ci#define TMP51X_SHUNT_CURRENT_L_LIMIT_POS 14 9562306a36Sopenharmony_ci#define TMP51X_BUS_VOLTAGE_H_LIMIT_POS 13 9662306a36Sopenharmony_ci#define TMP51X_BUS_VOLTAGE_L_LIMIT_POS 12 9762306a36Sopenharmony_ci#define TMP51X_POWER_LIMIT_POS 11 9862306a36Sopenharmony_ci#define TMP51X_LOCAL_TEMP_LIMIT_POS 10 9962306a36Sopenharmony_ci#define TMP51X_REMOTE_TEMP_LIMIT_1_POS 9 10062306a36Sopenharmony_ci#define TMP51X_REMOTE_TEMP_LIMIT_2_POS 8 10162306a36Sopenharmony_ci#define TMP513_REMOTE_TEMP_LIMIT_3_POS 7 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define TMP51X_VBUS_RANGE_32V 32000000 10462306a36Sopenharmony_ci#define TMP51X_VBUS_RANGE_16V 16000000 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci// Max and Min value 10762306a36Sopenharmony_ci#define MAX_BUS_VOLTAGE_32_LIMIT 32764 10862306a36Sopenharmony_ci#define MAX_BUS_VOLTAGE_16_LIMIT 16382 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci// Max possible value is -256 to +256 but datasheet indicated -40 to 125. 11162306a36Sopenharmony_ci#define MAX_TEMP_LIMIT 125000 11262306a36Sopenharmony_ci#define MIN_TEMP_LIMIT -40000 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define MAX_TEMP_HYST 127500 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic const u8 TMP51X_TEMP_INPUT[4] = { 11762306a36Sopenharmony_ci TMP51X_LOCAL_TEMP_RESULT, 11862306a36Sopenharmony_ci TMP51X_REMOTE_TEMP_RESULT_1, 11962306a36Sopenharmony_ci TMP51X_REMOTE_TEMP_RESULT_2, 12062306a36Sopenharmony_ci TMP513_REMOTE_TEMP_RESULT_3 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const u8 TMP51X_TEMP_CRIT[4] = { 12462306a36Sopenharmony_ci TMP51X_LOCAL_TEMP_LIMIT, 12562306a36Sopenharmony_ci TMP51X_REMOTE_TEMP_LIMIT_1, 12662306a36Sopenharmony_ci TMP51X_REMOTE_TEMP_LIMIT_2, 12762306a36Sopenharmony_ci TMP513_REMOTE_TEMP_LIMIT_3 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const u8 TMP51X_TEMP_CRIT_ALARM[4] = { 13162306a36Sopenharmony_ci TMP51X_LOCAL_TEMP_LIMIT_POS, 13262306a36Sopenharmony_ci TMP51X_REMOTE_TEMP_LIMIT_1_POS, 13362306a36Sopenharmony_ci TMP51X_REMOTE_TEMP_LIMIT_2_POS, 13462306a36Sopenharmony_ci TMP513_REMOTE_TEMP_LIMIT_3_POS 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic const u8 TMP51X_TEMP_CRIT_HYST[4] = { 13862306a36Sopenharmony_ci TMP51X_N_FACTOR_AND_HYST_1, 13962306a36Sopenharmony_ci TMP51X_N_FACTOR_AND_HYST_1, 14062306a36Sopenharmony_ci TMP51X_N_FACTOR_AND_HYST_1, 14162306a36Sopenharmony_ci TMP51X_N_FACTOR_AND_HYST_1 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic const u8 TMP51X_CURR_INPUT[2] = { 14562306a36Sopenharmony_ci TMP51X_SHUNT_CURRENT_RESULT, 14662306a36Sopenharmony_ci TMP51X_BUS_CURRENT_RESULT 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic struct regmap_config tmp51x_regmap_config = { 15062306a36Sopenharmony_ci .reg_bits = 8, 15162306a36Sopenharmony_ci .val_bits = 16, 15262306a36Sopenharmony_ci .max_register = TMP51X_MAX_REGISTER_ADDR, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cienum tmp51x_ids { 15662306a36Sopenharmony_ci tmp512, tmp513 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistruct tmp51x_data { 16062306a36Sopenharmony_ci u16 shunt_config; 16162306a36Sopenharmony_ci u16 pga_gain; 16262306a36Sopenharmony_ci u32 vbus_range_uvolt; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci u16 temp_config; 16562306a36Sopenharmony_ci u32 nfactor[3]; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci u32 shunt_uohms; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci u32 curr_lsb_ua; 17062306a36Sopenharmony_ci u32 pwr_lsb_uw; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci enum tmp51x_ids id; 17362306a36Sopenharmony_ci struct regmap *regmap; 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci// Set the shift based on the gain 8=4, 4=3, 2=2, 1=1 17762306a36Sopenharmony_cistatic inline u8 tmp51x_get_pga_shift(struct tmp51x_data *data) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci return 5 - ffs(data->pga_gain); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos, 18362306a36Sopenharmony_ci unsigned int regval, long *val) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci switch (reg) { 18662306a36Sopenharmony_ci case TMP51X_STATUS: 18762306a36Sopenharmony_ci *val = (regval >> pos) & 1; 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci case TMP51X_SHUNT_CURRENT_RESULT: 19062306a36Sopenharmony_ci case TMP51X_SHUNT_CURRENT_H_LIMIT: 19162306a36Sopenharmony_ci case TMP51X_SHUNT_CURRENT_L_LIMIT: 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * The valus is read in voltage in the chip but reported as 19462306a36Sopenharmony_ci * current to the user. 19562306a36Sopenharmony_ci * 2's complement number shifted by one to four depending 19662306a36Sopenharmony_ci * on the pga gain setting. 1lsb = 10uV 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci *val = sign_extend32(regval, 17 - tmp51x_get_pga_shift(data)); 19962306a36Sopenharmony_ci *val = DIV_ROUND_CLOSEST(*val * 10000, data->shunt_uohms); 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci case TMP51X_BUS_VOLTAGE_RESULT: 20262306a36Sopenharmony_ci case TMP51X_BUS_VOLTAGE_H_LIMIT: 20362306a36Sopenharmony_ci case TMP51X_BUS_VOLTAGE_L_LIMIT: 20462306a36Sopenharmony_ci // 1lsb = 4mV 20562306a36Sopenharmony_ci *val = (regval >> TMP51X_BUS_VOLTAGE_SHIFT) * 4; 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci case TMP51X_POWER_RESULT: 20862306a36Sopenharmony_ci case TMP51X_POWER_LIMIT: 20962306a36Sopenharmony_ci // Power = (current * BusVoltage) / 5000 21062306a36Sopenharmony_ci *val = regval * data->pwr_lsb_uw; 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci case TMP51X_BUS_CURRENT_RESULT: 21362306a36Sopenharmony_ci // Current = (ShuntVoltage * CalibrationRegister) / 4096 21462306a36Sopenharmony_ci *val = sign_extend32(regval, 16) * data->curr_lsb_ua; 21562306a36Sopenharmony_ci *val = DIV_ROUND_CLOSEST(*val, 1000); 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci case TMP51X_LOCAL_TEMP_RESULT: 21862306a36Sopenharmony_ci case TMP51X_REMOTE_TEMP_RESULT_1: 21962306a36Sopenharmony_ci case TMP51X_REMOTE_TEMP_RESULT_2: 22062306a36Sopenharmony_ci case TMP513_REMOTE_TEMP_RESULT_3: 22162306a36Sopenharmony_ci case TMP51X_LOCAL_TEMP_LIMIT: 22262306a36Sopenharmony_ci case TMP51X_REMOTE_TEMP_LIMIT_1: 22362306a36Sopenharmony_ci case TMP51X_REMOTE_TEMP_LIMIT_2: 22462306a36Sopenharmony_ci case TMP513_REMOTE_TEMP_LIMIT_3: 22562306a36Sopenharmony_ci // 1lsb = 0.0625 degrees centigrade 22662306a36Sopenharmony_ci *val = sign_extend32(regval, 16) >> TMP51X_TEMP_SHIFT; 22762306a36Sopenharmony_ci *val = DIV_ROUND_CLOSEST(*val * 625, 10); 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci case TMP51X_N_FACTOR_AND_HYST_1: 23062306a36Sopenharmony_ci // 1lsb = 0.5 degrees centigrade 23162306a36Sopenharmony_ci *val = (regval & TMP51X_HYST_MASK) * 500; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci default: 23462306a36Sopenharmony_ci // Programmer goofed 23562306a36Sopenharmony_ci WARN_ON_ONCE(1); 23662306a36Sopenharmony_ci *val = 0; 23762306a36Sopenharmony_ci return -EOPNOTSUPP; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int tmp51x_set_value(struct tmp51x_data *data, u8 reg, long val) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int regval, max_val; 24662306a36Sopenharmony_ci u32 mask = 0; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci switch (reg) { 24962306a36Sopenharmony_ci case TMP51X_SHUNT_CURRENT_H_LIMIT: 25062306a36Sopenharmony_ci case TMP51X_SHUNT_CURRENT_L_LIMIT: 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * The user enter current value and we convert it to 25362306a36Sopenharmony_ci * voltage. 1lsb = 10uV 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci val = DIV_ROUND_CLOSEST(val * data->shunt_uohms, 10000); 25662306a36Sopenharmony_ci max_val = U16_MAX >> tmp51x_get_pga_shift(data); 25762306a36Sopenharmony_ci regval = clamp_val(val, -max_val, max_val); 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci case TMP51X_BUS_VOLTAGE_H_LIMIT: 26062306a36Sopenharmony_ci case TMP51X_BUS_VOLTAGE_L_LIMIT: 26162306a36Sopenharmony_ci // 1lsb = 4mV 26262306a36Sopenharmony_ci max_val = (data->vbus_range_uvolt == TMP51X_VBUS_RANGE_32V) ? 26362306a36Sopenharmony_ci MAX_BUS_VOLTAGE_32_LIMIT : MAX_BUS_VOLTAGE_16_LIMIT; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci val = clamp_val(DIV_ROUND_CLOSEST(val, 4), 0, max_val); 26662306a36Sopenharmony_ci regval = val << TMP51X_BUS_VOLTAGE_SHIFT; 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case TMP51X_POWER_LIMIT: 26962306a36Sopenharmony_ci regval = clamp_val(DIV_ROUND_CLOSEST(val, data->pwr_lsb_uw), 0, 27062306a36Sopenharmony_ci U16_MAX); 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci case TMP51X_LOCAL_TEMP_LIMIT: 27362306a36Sopenharmony_ci case TMP51X_REMOTE_TEMP_LIMIT_1: 27462306a36Sopenharmony_ci case TMP51X_REMOTE_TEMP_LIMIT_2: 27562306a36Sopenharmony_ci case TMP513_REMOTE_TEMP_LIMIT_3: 27662306a36Sopenharmony_ci // 1lsb = 0.0625 degrees centigrade 27762306a36Sopenharmony_ci val = clamp_val(val, MIN_TEMP_LIMIT, MAX_TEMP_LIMIT); 27862306a36Sopenharmony_ci regval = DIV_ROUND_CLOSEST(val * 10, 625) << TMP51X_TEMP_SHIFT; 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci case TMP51X_N_FACTOR_AND_HYST_1: 28162306a36Sopenharmony_ci // 1lsb = 0.5 degrees centigrade 28262306a36Sopenharmony_ci val = clamp_val(val, 0, MAX_TEMP_HYST); 28362306a36Sopenharmony_ci regval = DIV_ROUND_CLOSEST(val, 500); 28462306a36Sopenharmony_ci mask = TMP51X_HYST_MASK; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci default: 28762306a36Sopenharmony_ci // Programmer goofed 28862306a36Sopenharmony_ci WARN_ON_ONCE(1); 28962306a36Sopenharmony_ci return -EOPNOTSUPP; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (mask == 0) 29362306a36Sopenharmony_ci return regmap_write(data->regmap, reg, regval); 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci return regmap_update_bits(data->regmap, reg, mask, regval); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic u8 tmp51x_get_reg(enum hwmon_sensor_types type, u32 attr, int channel) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci switch (type) { 30162306a36Sopenharmony_ci case hwmon_temp: 30262306a36Sopenharmony_ci switch (attr) { 30362306a36Sopenharmony_ci case hwmon_temp_input: 30462306a36Sopenharmony_ci return TMP51X_TEMP_INPUT[channel]; 30562306a36Sopenharmony_ci case hwmon_temp_crit_alarm: 30662306a36Sopenharmony_ci return TMP51X_STATUS; 30762306a36Sopenharmony_ci case hwmon_temp_crit: 30862306a36Sopenharmony_ci return TMP51X_TEMP_CRIT[channel]; 30962306a36Sopenharmony_ci case hwmon_temp_crit_hyst: 31062306a36Sopenharmony_ci return TMP51X_TEMP_CRIT_HYST[channel]; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case hwmon_in: 31462306a36Sopenharmony_ci switch (attr) { 31562306a36Sopenharmony_ci case hwmon_in_input: 31662306a36Sopenharmony_ci return TMP51X_BUS_VOLTAGE_RESULT; 31762306a36Sopenharmony_ci case hwmon_in_lcrit_alarm: 31862306a36Sopenharmony_ci case hwmon_in_crit_alarm: 31962306a36Sopenharmony_ci return TMP51X_STATUS; 32062306a36Sopenharmony_ci case hwmon_in_lcrit: 32162306a36Sopenharmony_ci return TMP51X_BUS_VOLTAGE_L_LIMIT; 32262306a36Sopenharmony_ci case hwmon_in_crit: 32362306a36Sopenharmony_ci return TMP51X_BUS_VOLTAGE_H_LIMIT; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case hwmon_curr: 32762306a36Sopenharmony_ci switch (attr) { 32862306a36Sopenharmony_ci case hwmon_curr_input: 32962306a36Sopenharmony_ci return TMP51X_CURR_INPUT[channel]; 33062306a36Sopenharmony_ci case hwmon_curr_lcrit_alarm: 33162306a36Sopenharmony_ci case hwmon_curr_crit_alarm: 33262306a36Sopenharmony_ci return TMP51X_STATUS; 33362306a36Sopenharmony_ci case hwmon_curr_lcrit: 33462306a36Sopenharmony_ci return TMP51X_SHUNT_CURRENT_L_LIMIT; 33562306a36Sopenharmony_ci case hwmon_curr_crit: 33662306a36Sopenharmony_ci return TMP51X_SHUNT_CURRENT_H_LIMIT; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci case hwmon_power: 34062306a36Sopenharmony_ci switch (attr) { 34162306a36Sopenharmony_ci case hwmon_power_input: 34262306a36Sopenharmony_ci return TMP51X_POWER_RESULT; 34362306a36Sopenharmony_ci case hwmon_power_crit_alarm: 34462306a36Sopenharmony_ci return TMP51X_STATUS; 34562306a36Sopenharmony_ci case hwmon_power_crit: 34662306a36Sopenharmony_ci return TMP51X_POWER_LIMIT; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci default: 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic u8 tmp51x_get_status_pos(enum hwmon_sensor_types type, u32 attr, 35762306a36Sopenharmony_ci int channel) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci switch (type) { 36062306a36Sopenharmony_ci case hwmon_temp: 36162306a36Sopenharmony_ci switch (attr) { 36262306a36Sopenharmony_ci case hwmon_temp_crit_alarm: 36362306a36Sopenharmony_ci return TMP51X_TEMP_CRIT_ALARM[channel]; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci case hwmon_in: 36762306a36Sopenharmony_ci switch (attr) { 36862306a36Sopenharmony_ci case hwmon_in_lcrit_alarm: 36962306a36Sopenharmony_ci return TMP51X_BUS_VOLTAGE_L_LIMIT_POS; 37062306a36Sopenharmony_ci case hwmon_in_crit_alarm: 37162306a36Sopenharmony_ci return TMP51X_BUS_VOLTAGE_H_LIMIT_POS; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci case hwmon_curr: 37562306a36Sopenharmony_ci switch (attr) { 37662306a36Sopenharmony_ci case hwmon_curr_lcrit_alarm: 37762306a36Sopenharmony_ci return TMP51X_SHUNT_CURRENT_L_LIMIT_POS; 37862306a36Sopenharmony_ci case hwmon_curr_crit_alarm: 37962306a36Sopenharmony_ci return TMP51X_SHUNT_CURRENT_H_LIMIT_POS; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci case hwmon_power: 38362306a36Sopenharmony_ci switch (attr) { 38462306a36Sopenharmony_ci case hwmon_power_crit_alarm: 38562306a36Sopenharmony_ci return TMP51X_POWER_LIMIT_POS; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci default: 38962306a36Sopenharmony_ci break; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic int tmp51x_read(struct device *dev, enum hwmon_sensor_types type, 39662306a36Sopenharmony_ci u32 attr, int channel, long *val) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct tmp51x_data *data = dev_get_drvdata(dev); 39962306a36Sopenharmony_ci int ret; 40062306a36Sopenharmony_ci u32 regval; 40162306a36Sopenharmony_ci u8 pos = 0, reg = 0; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci reg = tmp51x_get_reg(type, attr, channel); 40462306a36Sopenharmony_ci if (reg == 0) 40562306a36Sopenharmony_ci return -EOPNOTSUPP; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (reg == TMP51X_STATUS) 40862306a36Sopenharmony_ci pos = tmp51x_get_status_pos(type, attr, channel); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ret = regmap_read(data->regmap, reg, ®val); 41162306a36Sopenharmony_ci if (ret < 0) 41262306a36Sopenharmony_ci return ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return tmp51x_get_value(data, reg, pos, regval, val); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int tmp51x_write(struct device *dev, enum hwmon_sensor_types type, 41862306a36Sopenharmony_ci u32 attr, int channel, long val) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci u8 reg = 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci reg = tmp51x_get_reg(type, attr, channel); 42362306a36Sopenharmony_ci if (reg == 0) 42462306a36Sopenharmony_ci return -EOPNOTSUPP; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return tmp51x_set_value(dev_get_drvdata(dev), reg, val); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic umode_t tmp51x_is_visible(const void *_data, 43062306a36Sopenharmony_ci enum hwmon_sensor_types type, u32 attr, 43162306a36Sopenharmony_ci int channel) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci const struct tmp51x_data *data = _data; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci switch (type) { 43662306a36Sopenharmony_ci case hwmon_temp: 43762306a36Sopenharmony_ci if (data->id == tmp512 && channel == 3) 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci switch (attr) { 44062306a36Sopenharmony_ci case hwmon_temp_input: 44162306a36Sopenharmony_ci case hwmon_temp_crit_alarm: 44262306a36Sopenharmony_ci return 0444; 44362306a36Sopenharmony_ci case hwmon_temp_crit: 44462306a36Sopenharmony_ci return 0644; 44562306a36Sopenharmony_ci case hwmon_temp_crit_hyst: 44662306a36Sopenharmony_ci if (channel == 0) 44762306a36Sopenharmony_ci return 0644; 44862306a36Sopenharmony_ci return 0444; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci case hwmon_in: 45262306a36Sopenharmony_ci switch (attr) { 45362306a36Sopenharmony_ci case hwmon_in_input: 45462306a36Sopenharmony_ci case hwmon_in_lcrit_alarm: 45562306a36Sopenharmony_ci case hwmon_in_crit_alarm: 45662306a36Sopenharmony_ci return 0444; 45762306a36Sopenharmony_ci case hwmon_in_lcrit: 45862306a36Sopenharmony_ci case hwmon_in_crit: 45962306a36Sopenharmony_ci return 0644; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case hwmon_curr: 46362306a36Sopenharmony_ci if (!data->shunt_uohms) 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci switch (attr) { 46762306a36Sopenharmony_ci case hwmon_curr_input: 46862306a36Sopenharmony_ci case hwmon_curr_lcrit_alarm: 46962306a36Sopenharmony_ci case hwmon_curr_crit_alarm: 47062306a36Sopenharmony_ci return 0444; 47162306a36Sopenharmony_ci case hwmon_curr_lcrit: 47262306a36Sopenharmony_ci case hwmon_curr_crit: 47362306a36Sopenharmony_ci return 0644; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci case hwmon_power: 47762306a36Sopenharmony_ci if (!data->shunt_uohms) 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci switch (attr) { 48162306a36Sopenharmony_ci case hwmon_power_input: 48262306a36Sopenharmony_ci case hwmon_power_crit_alarm: 48362306a36Sopenharmony_ci return 0444; 48462306a36Sopenharmony_ci case hwmon_power_crit: 48562306a36Sopenharmony_ci return 0644; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci default: 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic const struct hwmon_channel_info * const tmp51x_info[] = { 49562306a36Sopenharmony_ci HWMON_CHANNEL_INFO(temp, 49662306a36Sopenharmony_ci HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | 49762306a36Sopenharmony_ci HWMON_T_CRIT_HYST, 49862306a36Sopenharmony_ci HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | 49962306a36Sopenharmony_ci HWMON_T_CRIT_HYST, 50062306a36Sopenharmony_ci HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | 50162306a36Sopenharmony_ci HWMON_T_CRIT_HYST, 50262306a36Sopenharmony_ci HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | 50362306a36Sopenharmony_ci HWMON_T_CRIT_HYST), 50462306a36Sopenharmony_ci HWMON_CHANNEL_INFO(in, 50562306a36Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LCRIT | HWMON_I_LCRIT_ALARM | 50662306a36Sopenharmony_ci HWMON_I_CRIT | HWMON_I_CRIT_ALARM), 50762306a36Sopenharmony_ci HWMON_CHANNEL_INFO(curr, 50862306a36Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LCRIT | HWMON_C_LCRIT_ALARM | 50962306a36Sopenharmony_ci HWMON_C_CRIT | HWMON_C_CRIT_ALARM, 51062306a36Sopenharmony_ci HWMON_C_INPUT), 51162306a36Sopenharmony_ci HWMON_CHANNEL_INFO(power, 51262306a36Sopenharmony_ci HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM), 51362306a36Sopenharmony_ci NULL 51462306a36Sopenharmony_ci}; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic const struct hwmon_ops tmp51x_hwmon_ops = { 51762306a36Sopenharmony_ci .is_visible = tmp51x_is_visible, 51862306a36Sopenharmony_ci .read = tmp51x_read, 51962306a36Sopenharmony_ci .write = tmp51x_write, 52062306a36Sopenharmony_ci}; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic const struct hwmon_chip_info tmp51x_chip_info = { 52362306a36Sopenharmony_ci .ops = &tmp51x_hwmon_ops, 52462306a36Sopenharmony_ci .info = tmp51x_info, 52562306a36Sopenharmony_ci}; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/* 52862306a36Sopenharmony_ci * Calibrate the tmp51x following the datasheet method 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_cistatic int tmp51x_calibrate(struct tmp51x_data *data) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci int vshunt_max = data->pga_gain * 40; 53362306a36Sopenharmony_ci u64 max_curr_ma; 53462306a36Sopenharmony_ci u32 div; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* 53762306a36Sopenharmony_ci * If shunt_uohms is equal to 0, the calibration should be set to 0. 53862306a36Sopenharmony_ci * The consequence will be that the current and power measurement engine 53962306a36Sopenharmony_ci * of the sensor will not work. Temperature and voltage sensing will 54062306a36Sopenharmony_ci * continue to work. 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci if (data->shunt_uohms == 0) 54362306a36Sopenharmony_ci return regmap_write(data->regmap, TMP51X_SHUNT_CALIBRATION, 0); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci max_curr_ma = DIV_ROUND_CLOSEST_ULL(vshunt_max * 1000 * 1000, 54662306a36Sopenharmony_ci data->shunt_uohms); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* 54962306a36Sopenharmony_ci * Calculate the minimal bit resolution for the current and the power. 55062306a36Sopenharmony_ci * Those values will be used during register interpretation. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci data->curr_lsb_ua = DIV_ROUND_CLOSEST_ULL(max_curr_ma * 1000, 32767); 55362306a36Sopenharmony_ci data->pwr_lsb_uw = 20 * data->curr_lsb_ua; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci div = DIV_ROUND_CLOSEST_ULL(data->curr_lsb_ua * data->shunt_uohms, 55662306a36Sopenharmony_ci 1000 * 1000); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return regmap_write(data->regmap, TMP51X_SHUNT_CALIBRATION, 55962306a36Sopenharmony_ci DIV_ROUND_CLOSEST(40960, div)); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci/* 56362306a36Sopenharmony_ci * Initialize the configuration and calibration registers. 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_cistatic int tmp51x_init(struct tmp51x_data *data) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci unsigned int regval; 56862306a36Sopenharmony_ci int ret = regmap_write(data->regmap, TMP51X_SHUNT_CONFIG, 56962306a36Sopenharmony_ci data->shunt_config); 57062306a36Sopenharmony_ci if (ret < 0) 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci ret = regmap_write(data->regmap, TMP51X_TEMP_CONFIG, data->temp_config); 57462306a36Sopenharmony_ci if (ret < 0) 57562306a36Sopenharmony_ci return ret; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci // nFactor configuration 57862306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, TMP51X_N_FACTOR_AND_HYST_1, 57962306a36Sopenharmony_ci TMP51X_NFACTOR_MASK, data->nfactor[0] << 8); 58062306a36Sopenharmony_ci if (ret < 0) 58162306a36Sopenharmony_ci return ret; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci ret = regmap_write(data->regmap, TMP51X_N_FACTOR_2, 58462306a36Sopenharmony_ci data->nfactor[1] << 8); 58562306a36Sopenharmony_ci if (ret < 0) 58662306a36Sopenharmony_ci return ret; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (data->id == tmp513) { 58962306a36Sopenharmony_ci ret = regmap_write(data->regmap, TMP513_N_FACTOR_3, 59062306a36Sopenharmony_ci data->nfactor[2] << 8); 59162306a36Sopenharmony_ci if (ret < 0) 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci ret = tmp51x_calibrate(data); 59662306a36Sopenharmony_ci if (ret < 0) 59762306a36Sopenharmony_ci return ret; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci // Read the status register before using as the datasheet propose 60062306a36Sopenharmony_ci return regmap_read(data->regmap, TMP51X_STATUS, ®val); 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic const struct i2c_device_id tmp51x_id[] = { 60462306a36Sopenharmony_ci { "tmp512", tmp512 }, 60562306a36Sopenharmony_ci { "tmp513", tmp513 }, 60662306a36Sopenharmony_ci { } 60762306a36Sopenharmony_ci}; 60862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tmp51x_id); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic const struct of_device_id tmp51x_of_match[] = { 61162306a36Sopenharmony_ci { 61262306a36Sopenharmony_ci .compatible = "ti,tmp512", 61362306a36Sopenharmony_ci .data = (void *)tmp512 61462306a36Sopenharmony_ci }, 61562306a36Sopenharmony_ci { 61662306a36Sopenharmony_ci .compatible = "ti,tmp513", 61762306a36Sopenharmony_ci .data = (void *)tmp513 61862306a36Sopenharmony_ci }, 61962306a36Sopenharmony_ci { }, 62062306a36Sopenharmony_ci}; 62162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tmp51x_of_match); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int tmp51x_vbus_range_to_reg(struct device *dev, 62462306a36Sopenharmony_ci struct tmp51x_data *data) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci if (data->vbus_range_uvolt == TMP51X_VBUS_RANGE_32V) { 62762306a36Sopenharmony_ci data->shunt_config |= TMP51X_BUS_VOLTAGE_MASK; 62862306a36Sopenharmony_ci } else if (data->vbus_range_uvolt == TMP51X_VBUS_RANGE_16V) { 62962306a36Sopenharmony_ci data->shunt_config &= ~TMP51X_BUS_VOLTAGE_MASK; 63062306a36Sopenharmony_ci } else { 63162306a36Sopenharmony_ci dev_err(dev, "ti,bus-range-microvolt is invalid: %u\n", 63262306a36Sopenharmony_ci data->vbus_range_uvolt); 63362306a36Sopenharmony_ci return -EINVAL; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic int tmp51x_pga_gain_to_reg(struct device *dev, struct tmp51x_data *data) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci if (data->pga_gain == 8) { 64162306a36Sopenharmony_ci data->shunt_config |= CURRENT_SENSE_VOLTAGE_320_MASK; 64262306a36Sopenharmony_ci } else if (data->pga_gain == 4) { 64362306a36Sopenharmony_ci data->shunt_config |= CURRENT_SENSE_VOLTAGE_160_MASK; 64462306a36Sopenharmony_ci } else if (data->pga_gain == 2) { 64562306a36Sopenharmony_ci data->shunt_config |= CURRENT_SENSE_VOLTAGE_80_MASK; 64662306a36Sopenharmony_ci } else if (data->pga_gain == 1) { 64762306a36Sopenharmony_ci data->shunt_config |= CURRENT_SENSE_VOLTAGE_40_MASK; 64862306a36Sopenharmony_ci } else { 64962306a36Sopenharmony_ci dev_err(dev, "ti,pga-gain is invalid: %u\n", data->pga_gain); 65062306a36Sopenharmony_ci return -EINVAL; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci return 0; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic int tmp51x_read_properties(struct device *dev, struct tmp51x_data *data) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci int ret; 65862306a36Sopenharmony_ci u32 nfactor[3]; 65962306a36Sopenharmony_ci u32 val; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", &val); 66262306a36Sopenharmony_ci data->shunt_uohms = (ret >= 0) ? val : TMP51X_SHUNT_VALUE_DEFAULT; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci ret = device_property_read_u32(dev, "ti,bus-range-microvolt", &val); 66562306a36Sopenharmony_ci data->vbus_range_uvolt = (ret >= 0) ? val : TMP51X_VBUS_RANGE_DEFAULT; 66662306a36Sopenharmony_ci ret = tmp51x_vbus_range_to_reg(dev, data); 66762306a36Sopenharmony_ci if (ret < 0) 66862306a36Sopenharmony_ci return ret; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ret = device_property_read_u32(dev, "ti,pga-gain", &val); 67162306a36Sopenharmony_ci data->pga_gain = (ret >= 0) ? val : TMP51X_PGA_DEFAULT; 67262306a36Sopenharmony_ci ret = tmp51x_pga_gain_to_reg(dev, data); 67362306a36Sopenharmony_ci if (ret < 0) 67462306a36Sopenharmony_ci return ret; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = device_property_read_u32_array(dev, "ti,nfactor", nfactor, 67762306a36Sopenharmony_ci (data->id == tmp513) ? 3 : 2); 67862306a36Sopenharmony_ci if (ret >= 0) 67962306a36Sopenharmony_ci memcpy(data->nfactor, nfactor, (data->id == tmp513) ? 3 : 2); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci // Check if shunt value is compatible with pga-gain 68262306a36Sopenharmony_ci if (data->shunt_uohms > data->pga_gain * 40 * 1000 * 1000) { 68362306a36Sopenharmony_ci dev_err(dev, "shunt-resistor: %u too big for pga_gain: %u\n", 68462306a36Sopenharmony_ci data->shunt_uohms, data->pga_gain); 68562306a36Sopenharmony_ci return -EINVAL; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic void tmp51x_use_default(struct tmp51x_data *data) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci data->vbus_range_uvolt = TMP51X_VBUS_RANGE_DEFAULT; 69462306a36Sopenharmony_ci data->pga_gain = TMP51X_PGA_DEFAULT; 69562306a36Sopenharmony_ci data->shunt_uohms = TMP51X_SHUNT_VALUE_DEFAULT; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int tmp51x_configure(struct device *dev, struct tmp51x_data *data) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci data->shunt_config = TMP51X_SHUNT_CONFIG_DEFAULT; 70162306a36Sopenharmony_ci data->temp_config = (data->id == tmp513) ? 70262306a36Sopenharmony_ci TMP513_TEMP_CONFIG_DEFAULT : TMP512_TEMP_CONFIG_DEFAULT; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (dev->of_node) 70562306a36Sopenharmony_ci return tmp51x_read_properties(dev, data); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci tmp51x_use_default(data); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int tmp51x_probe(struct i2c_client *client) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct device *dev = &client->dev; 71562306a36Sopenharmony_ci struct tmp51x_data *data; 71662306a36Sopenharmony_ci struct device *hwmon_dev; 71762306a36Sopenharmony_ci int ret; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 72062306a36Sopenharmony_ci if (!data) 72162306a36Sopenharmony_ci return -ENOMEM; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci data->id = (uintptr_t)i2c_get_match_data(client); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci ret = tmp51x_configure(dev, data); 72662306a36Sopenharmony_ci if (ret < 0) { 72762306a36Sopenharmony_ci dev_err(dev, "error configuring the device: %d\n", ret); 72862306a36Sopenharmony_ci return ret; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci data->regmap = devm_regmap_init_i2c(client, &tmp51x_regmap_config); 73262306a36Sopenharmony_ci if (IS_ERR(data->regmap)) { 73362306a36Sopenharmony_ci dev_err(dev, "failed to allocate register map\n"); 73462306a36Sopenharmony_ci return PTR_ERR(data->regmap); 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ret = tmp51x_init(data); 73862306a36Sopenharmony_ci if (ret < 0) { 73962306a36Sopenharmony_ci dev_err(dev, "error configuring the device: %d\n", ret); 74062306a36Sopenharmony_ci return -ENODEV; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, 74462306a36Sopenharmony_ci data, 74562306a36Sopenharmony_ci &tmp51x_chip_info, 74662306a36Sopenharmony_ci NULL); 74762306a36Sopenharmony_ci if (IS_ERR(hwmon_dev)) 74862306a36Sopenharmony_ci return PTR_ERR(hwmon_dev); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci dev_dbg(dev, "power monitor %s\n", client->name); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci return 0; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic struct i2c_driver tmp51x_driver = { 75662306a36Sopenharmony_ci .driver = { 75762306a36Sopenharmony_ci .name = "tmp51x", 75862306a36Sopenharmony_ci .of_match_table = tmp51x_of_match, 75962306a36Sopenharmony_ci }, 76062306a36Sopenharmony_ci .probe = tmp51x_probe, 76162306a36Sopenharmony_ci .id_table = tmp51x_id, 76262306a36Sopenharmony_ci}; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cimodule_i2c_driver(tmp51x_driver); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ciMODULE_AUTHOR("Eric Tremblay <etremblay@distechcontrols.com>"); 76762306a36Sopenharmony_ciMODULE_DESCRIPTION("tmp51x driver"); 76862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 769