162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2020 Sartura Ltd. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Driver for the TI TPS23861 PoE PSE. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Robert Marko <robert.marko@sartura.hr> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bitfield.h> 1162306a36Sopenharmony_ci#include <linux/debugfs.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 1462306a36Sopenharmony_ci#include <linux/hwmon.h> 1562306a36Sopenharmony_ci#include <linux/i2c.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define TEMPERATURE 0x2c 2162306a36Sopenharmony_ci#define INPUT_VOLTAGE_LSB 0x2e 2262306a36Sopenharmony_ci#define INPUT_VOLTAGE_MSB 0x2f 2362306a36Sopenharmony_ci#define PORT_1_CURRENT_LSB 0x30 2462306a36Sopenharmony_ci#define PORT_1_CURRENT_MSB 0x31 2562306a36Sopenharmony_ci#define PORT_1_VOLTAGE_LSB 0x32 2662306a36Sopenharmony_ci#define PORT_1_VOLTAGE_MSB 0x33 2762306a36Sopenharmony_ci#define PORT_2_CURRENT_LSB 0x34 2862306a36Sopenharmony_ci#define PORT_2_CURRENT_MSB 0x35 2962306a36Sopenharmony_ci#define PORT_2_VOLTAGE_LSB 0x36 3062306a36Sopenharmony_ci#define PORT_2_VOLTAGE_MSB 0x37 3162306a36Sopenharmony_ci#define PORT_3_CURRENT_LSB 0x38 3262306a36Sopenharmony_ci#define PORT_3_CURRENT_MSB 0x39 3362306a36Sopenharmony_ci#define PORT_3_VOLTAGE_LSB 0x3a 3462306a36Sopenharmony_ci#define PORT_3_VOLTAGE_MSB 0x3b 3562306a36Sopenharmony_ci#define PORT_4_CURRENT_LSB 0x3c 3662306a36Sopenharmony_ci#define PORT_4_CURRENT_MSB 0x3d 3762306a36Sopenharmony_ci#define PORT_4_VOLTAGE_LSB 0x3e 3862306a36Sopenharmony_ci#define PORT_4_VOLTAGE_MSB 0x3f 3962306a36Sopenharmony_ci#define PORT_N_CURRENT_LSB_OFFSET 0x04 4062306a36Sopenharmony_ci#define PORT_N_VOLTAGE_LSB_OFFSET 0x04 4162306a36Sopenharmony_ci#define VOLTAGE_CURRENT_MASK GENMASK(13, 0) 4262306a36Sopenharmony_ci#define PORT_1_RESISTANCE_LSB 0x60 4362306a36Sopenharmony_ci#define PORT_1_RESISTANCE_MSB 0x61 4462306a36Sopenharmony_ci#define PORT_2_RESISTANCE_LSB 0x62 4562306a36Sopenharmony_ci#define PORT_2_RESISTANCE_MSB 0x63 4662306a36Sopenharmony_ci#define PORT_3_RESISTANCE_LSB 0x64 4762306a36Sopenharmony_ci#define PORT_3_RESISTANCE_MSB 0x65 4862306a36Sopenharmony_ci#define PORT_4_RESISTANCE_LSB 0x66 4962306a36Sopenharmony_ci#define PORT_4_RESISTANCE_MSB 0x67 5062306a36Sopenharmony_ci#define PORT_N_RESISTANCE_LSB_OFFSET 0x02 5162306a36Sopenharmony_ci#define PORT_RESISTANCE_MASK GENMASK(13, 0) 5262306a36Sopenharmony_ci#define PORT_RESISTANCE_RSN_MASK GENMASK(15, 14) 5362306a36Sopenharmony_ci#define PORT_RESISTANCE_RSN_OTHER 0 5462306a36Sopenharmony_ci#define PORT_RESISTANCE_RSN_LOW 1 5562306a36Sopenharmony_ci#define PORT_RESISTANCE_RSN_OPEN 2 5662306a36Sopenharmony_ci#define PORT_RESISTANCE_RSN_SHORT 3 5762306a36Sopenharmony_ci#define PORT_1_STATUS 0x0c 5862306a36Sopenharmony_ci#define PORT_2_STATUS 0x0d 5962306a36Sopenharmony_ci#define PORT_3_STATUS 0x0e 6062306a36Sopenharmony_ci#define PORT_4_STATUS 0x0f 6162306a36Sopenharmony_ci#define PORT_STATUS_CLASS_MASK GENMASK(7, 4) 6262306a36Sopenharmony_ci#define PORT_STATUS_DETECT_MASK GENMASK(3, 0) 6362306a36Sopenharmony_ci#define PORT_CLASS_UNKNOWN 0 6462306a36Sopenharmony_ci#define PORT_CLASS_1 1 6562306a36Sopenharmony_ci#define PORT_CLASS_2 2 6662306a36Sopenharmony_ci#define PORT_CLASS_3 3 6762306a36Sopenharmony_ci#define PORT_CLASS_4 4 6862306a36Sopenharmony_ci#define PORT_CLASS_RESERVED 5 6962306a36Sopenharmony_ci#define PORT_CLASS_0 6 7062306a36Sopenharmony_ci#define PORT_CLASS_OVERCURRENT 7 7162306a36Sopenharmony_ci#define PORT_CLASS_MISMATCH 8 7262306a36Sopenharmony_ci#define PORT_DETECT_UNKNOWN 0 7362306a36Sopenharmony_ci#define PORT_DETECT_SHORT 1 7462306a36Sopenharmony_ci#define PORT_DETECT_RESERVED 2 7562306a36Sopenharmony_ci#define PORT_DETECT_RESISTANCE_LOW 3 7662306a36Sopenharmony_ci#define PORT_DETECT_RESISTANCE_OK 4 7762306a36Sopenharmony_ci#define PORT_DETECT_RESISTANCE_HIGH 5 7862306a36Sopenharmony_ci#define PORT_DETECT_OPEN_CIRCUIT 6 7962306a36Sopenharmony_ci#define PORT_DETECT_RESERVED_2 7 8062306a36Sopenharmony_ci#define PORT_DETECT_MOSFET_FAULT 8 8162306a36Sopenharmony_ci#define PORT_DETECT_LEGACY 9 8262306a36Sopenharmony_ci/* Measurment beyond clamp voltage */ 8362306a36Sopenharmony_ci#define PORT_DETECT_CAPACITANCE_INVALID_BEYOND 10 8462306a36Sopenharmony_ci/* Insufficient voltage delta */ 8562306a36Sopenharmony_ci#define PORT_DETECT_CAPACITANCE_INVALID_DELTA 11 8662306a36Sopenharmony_ci#define PORT_DETECT_CAPACITANCE_OUT_OF_RANGE 12 8762306a36Sopenharmony_ci#define POE_PLUS 0x40 8862306a36Sopenharmony_ci#define OPERATING_MODE 0x12 8962306a36Sopenharmony_ci#define OPERATING_MODE_OFF 0 9062306a36Sopenharmony_ci#define OPERATING_MODE_MANUAL 1 9162306a36Sopenharmony_ci#define OPERATING_MODE_SEMI 2 9262306a36Sopenharmony_ci#define OPERATING_MODE_AUTO 3 9362306a36Sopenharmony_ci#define OPERATING_MODE_PORT_1_MASK GENMASK(1, 0) 9462306a36Sopenharmony_ci#define OPERATING_MODE_PORT_2_MASK GENMASK(3, 2) 9562306a36Sopenharmony_ci#define OPERATING_MODE_PORT_3_MASK GENMASK(5, 4) 9662306a36Sopenharmony_ci#define OPERATING_MODE_PORT_4_MASK GENMASK(7, 6) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define DETECT_CLASS_RESTART 0x18 9962306a36Sopenharmony_ci#define POWER_ENABLE 0x19 10062306a36Sopenharmony_ci#define TPS23861_NUM_PORTS 4 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define TPS23861_GENERAL_MASK_1 0x17 10362306a36Sopenharmony_ci#define TPS23861_CURRENT_SHUNT_MASK BIT(0) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define TEMPERATURE_LSB 652 /* 0.652 degrees Celsius */ 10662306a36Sopenharmony_ci#define VOLTAGE_LSB 3662 /* 3.662 mV */ 10762306a36Sopenharmony_ci#define SHUNT_RESISTOR_DEFAULT 255000 /* 255 mOhm */ 10862306a36Sopenharmony_ci#define CURRENT_LSB_250 62260 /* 62.260 uA */ 10962306a36Sopenharmony_ci#define CURRENT_LSB_255 61039 /* 61.039 uA */ 11062306a36Sopenharmony_ci#define RESISTANCE_LSB 110966 /* 11.0966 Ohm*/ 11162306a36Sopenharmony_ci#define RESISTANCE_LSB_LOW 157216 /* 15.7216 Ohm*/ 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistruct tps23861_data { 11462306a36Sopenharmony_ci struct regmap *regmap; 11562306a36Sopenharmony_ci u32 shunt_resistor; 11662306a36Sopenharmony_ci struct i2c_client *client; 11762306a36Sopenharmony_ci struct dentry *debugfs_dir; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic struct regmap_config tps23861_regmap_config = { 12162306a36Sopenharmony_ci .reg_bits = 8, 12262306a36Sopenharmony_ci .val_bits = 8, 12362306a36Sopenharmony_ci .max_register = 0x6f, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int tps23861_read_temp(struct tps23861_data *data, long *val) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci unsigned int regval; 12962306a36Sopenharmony_ci int err; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci err = regmap_read(data->regmap, TEMPERATURE, ®val); 13262306a36Sopenharmony_ci if (err < 0) 13362306a36Sopenharmony_ci return err; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci *val = (regval * TEMPERATURE_LSB) - 20000; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int tps23861_read_voltage(struct tps23861_data *data, int channel, 14162306a36Sopenharmony_ci long *val) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci __le16 regval; 14462306a36Sopenharmony_ci long raw_val; 14562306a36Sopenharmony_ci int err; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (channel < TPS23861_NUM_PORTS) { 14862306a36Sopenharmony_ci err = regmap_bulk_read(data->regmap, 14962306a36Sopenharmony_ci PORT_1_VOLTAGE_LSB + channel * PORT_N_VOLTAGE_LSB_OFFSET, 15062306a36Sopenharmony_ci ®val, 2); 15162306a36Sopenharmony_ci } else { 15262306a36Sopenharmony_ci err = regmap_bulk_read(data->regmap, 15362306a36Sopenharmony_ci INPUT_VOLTAGE_LSB, 15462306a36Sopenharmony_ci ®val, 2); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci if (err < 0) 15762306a36Sopenharmony_ci return err; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci raw_val = le16_to_cpu(regval); 16062306a36Sopenharmony_ci *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * VOLTAGE_LSB) / 1000; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int tps23861_read_current(struct tps23861_data *data, int channel, 16662306a36Sopenharmony_ci long *val) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci long raw_val, current_lsb; 16962306a36Sopenharmony_ci __le16 regval; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci int err; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT) 17462306a36Sopenharmony_ci current_lsb = CURRENT_LSB_255; 17562306a36Sopenharmony_ci else 17662306a36Sopenharmony_ci current_lsb = CURRENT_LSB_250; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci err = regmap_bulk_read(data->regmap, 17962306a36Sopenharmony_ci PORT_1_CURRENT_LSB + channel * PORT_N_CURRENT_LSB_OFFSET, 18062306a36Sopenharmony_ci ®val, 2); 18162306a36Sopenharmony_ci if (err < 0) 18262306a36Sopenharmony_ci return err; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci raw_val = le16_to_cpu(regval); 18562306a36Sopenharmony_ci *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * current_lsb) / 1000000; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int tps23861_port_disable(struct tps23861_data *data, int channel) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci unsigned int regval = 0; 19362306a36Sopenharmony_ci int err; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci regval |= BIT(channel + 4); 19662306a36Sopenharmony_ci err = regmap_write(data->regmap, POWER_ENABLE, regval); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return err; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int tps23861_port_enable(struct tps23861_data *data, int channel) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci unsigned int regval = 0; 20462306a36Sopenharmony_ci int err; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci regval |= BIT(channel); 20762306a36Sopenharmony_ci regval |= BIT(channel + 4); 20862306a36Sopenharmony_ci err = regmap_write(data->regmap, DETECT_CLASS_RESTART, regval); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return err; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic umode_t tps23861_is_visible(const void *data, enum hwmon_sensor_types type, 21462306a36Sopenharmony_ci u32 attr, int channel) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci switch (type) { 21762306a36Sopenharmony_ci case hwmon_temp: 21862306a36Sopenharmony_ci switch (attr) { 21962306a36Sopenharmony_ci case hwmon_temp_input: 22062306a36Sopenharmony_ci case hwmon_temp_label: 22162306a36Sopenharmony_ci return 0444; 22262306a36Sopenharmony_ci default: 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci case hwmon_in: 22662306a36Sopenharmony_ci switch (attr) { 22762306a36Sopenharmony_ci case hwmon_in_input: 22862306a36Sopenharmony_ci case hwmon_in_label: 22962306a36Sopenharmony_ci return 0444; 23062306a36Sopenharmony_ci case hwmon_in_enable: 23162306a36Sopenharmony_ci return 0200; 23262306a36Sopenharmony_ci default: 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci case hwmon_curr: 23662306a36Sopenharmony_ci switch (attr) { 23762306a36Sopenharmony_ci case hwmon_curr_input: 23862306a36Sopenharmony_ci case hwmon_curr_label: 23962306a36Sopenharmony_ci return 0444; 24062306a36Sopenharmony_ci default: 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci default: 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int tps23861_write(struct device *dev, enum hwmon_sensor_types type, 24962306a36Sopenharmony_ci u32 attr, int channel, long val) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct tps23861_data *data = dev_get_drvdata(dev); 25262306a36Sopenharmony_ci int err; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci switch (type) { 25562306a36Sopenharmony_ci case hwmon_in: 25662306a36Sopenharmony_ci switch (attr) { 25762306a36Sopenharmony_ci case hwmon_in_enable: 25862306a36Sopenharmony_ci if (val == 0) 25962306a36Sopenharmony_ci err = tps23861_port_disable(data, channel); 26062306a36Sopenharmony_ci else if (val == 1) 26162306a36Sopenharmony_ci err = tps23861_port_enable(data, channel); 26262306a36Sopenharmony_ci else 26362306a36Sopenharmony_ci err = -EINVAL; 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci default: 26662306a36Sopenharmony_ci return -EOPNOTSUPP; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci default: 27062306a36Sopenharmony_ci return -EOPNOTSUPP; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return err; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int tps23861_read(struct device *dev, enum hwmon_sensor_types type, 27762306a36Sopenharmony_ci u32 attr, int channel, long *val) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct tps23861_data *data = dev_get_drvdata(dev); 28062306a36Sopenharmony_ci int err; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci switch (type) { 28362306a36Sopenharmony_ci case hwmon_temp: 28462306a36Sopenharmony_ci switch (attr) { 28562306a36Sopenharmony_ci case hwmon_temp_input: 28662306a36Sopenharmony_ci err = tps23861_read_temp(data, val); 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci default: 28962306a36Sopenharmony_ci return -EOPNOTSUPP; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci case hwmon_in: 29362306a36Sopenharmony_ci switch (attr) { 29462306a36Sopenharmony_ci case hwmon_in_input: 29562306a36Sopenharmony_ci err = tps23861_read_voltage(data, channel, val); 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci default: 29862306a36Sopenharmony_ci return -EOPNOTSUPP; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci case hwmon_curr: 30262306a36Sopenharmony_ci switch (attr) { 30362306a36Sopenharmony_ci case hwmon_curr_input: 30462306a36Sopenharmony_ci err = tps23861_read_current(data, channel, val); 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci default: 30762306a36Sopenharmony_ci return -EOPNOTSUPP; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci default: 31162306a36Sopenharmony_ci return -EOPNOTSUPP; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return err; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic const char * const tps23861_port_label[] = { 31862306a36Sopenharmony_ci "Port1", 31962306a36Sopenharmony_ci "Port2", 32062306a36Sopenharmony_ci "Port3", 32162306a36Sopenharmony_ci "Port4", 32262306a36Sopenharmony_ci "Input", 32362306a36Sopenharmony_ci}; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int tps23861_read_string(struct device *dev, 32662306a36Sopenharmony_ci enum hwmon_sensor_types type, 32762306a36Sopenharmony_ci u32 attr, int channel, const char **str) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci switch (type) { 33062306a36Sopenharmony_ci case hwmon_in: 33162306a36Sopenharmony_ci case hwmon_curr: 33262306a36Sopenharmony_ci *str = tps23861_port_label[channel]; 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci case hwmon_temp: 33562306a36Sopenharmony_ci *str = "Die"; 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci default: 33862306a36Sopenharmony_ci return -EOPNOTSUPP; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic const struct hwmon_channel_info * const tps23861_info[] = { 34562306a36Sopenharmony_ci HWMON_CHANNEL_INFO(chip, 34662306a36Sopenharmony_ci HWMON_C_REGISTER_TZ), 34762306a36Sopenharmony_ci HWMON_CHANNEL_INFO(temp, 34862306a36Sopenharmony_ci HWMON_T_INPUT | HWMON_T_LABEL), 34962306a36Sopenharmony_ci HWMON_CHANNEL_INFO(in, 35062306a36Sopenharmony_ci HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 35162306a36Sopenharmony_ci HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 35262306a36Sopenharmony_ci HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 35362306a36Sopenharmony_ci HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 35462306a36Sopenharmony_ci HWMON_I_INPUT | HWMON_I_LABEL), 35562306a36Sopenharmony_ci HWMON_CHANNEL_INFO(curr, 35662306a36Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL, 35762306a36Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL, 35862306a36Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL, 35962306a36Sopenharmony_ci HWMON_C_INPUT | HWMON_C_LABEL), 36062306a36Sopenharmony_ci NULL 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic const struct hwmon_ops tps23861_hwmon_ops = { 36462306a36Sopenharmony_ci .is_visible = tps23861_is_visible, 36562306a36Sopenharmony_ci .write = tps23861_write, 36662306a36Sopenharmony_ci .read = tps23861_read, 36762306a36Sopenharmony_ci .read_string = tps23861_read_string, 36862306a36Sopenharmony_ci}; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic const struct hwmon_chip_info tps23861_chip_info = { 37162306a36Sopenharmony_ci .ops = &tps23861_hwmon_ops, 37262306a36Sopenharmony_ci .info = tps23861_info, 37362306a36Sopenharmony_ci}; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic char *port_operating_mode_string(uint8_t mode_reg, unsigned int port) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci unsigned int mode = ~0; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (port < TPS23861_NUM_PORTS) 38062306a36Sopenharmony_ci mode = (mode_reg >> (2 * port)) & OPERATING_MODE_PORT_1_MASK; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci switch (mode) { 38362306a36Sopenharmony_ci case OPERATING_MODE_OFF: 38462306a36Sopenharmony_ci return "Off"; 38562306a36Sopenharmony_ci case OPERATING_MODE_MANUAL: 38662306a36Sopenharmony_ci return "Manual"; 38762306a36Sopenharmony_ci case OPERATING_MODE_SEMI: 38862306a36Sopenharmony_ci return "Semi-Auto"; 38962306a36Sopenharmony_ci case OPERATING_MODE_AUTO: 39062306a36Sopenharmony_ci return "Auto"; 39162306a36Sopenharmony_ci default: 39262306a36Sopenharmony_ci return "Invalid"; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic char *port_detect_status_string(uint8_t status_reg) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci switch (FIELD_GET(PORT_STATUS_DETECT_MASK, status_reg)) { 39962306a36Sopenharmony_ci case PORT_DETECT_UNKNOWN: 40062306a36Sopenharmony_ci return "Unknown device"; 40162306a36Sopenharmony_ci case PORT_DETECT_SHORT: 40262306a36Sopenharmony_ci return "Short circuit"; 40362306a36Sopenharmony_ci case PORT_DETECT_RESISTANCE_LOW: 40462306a36Sopenharmony_ci return "Too low resistance"; 40562306a36Sopenharmony_ci case PORT_DETECT_RESISTANCE_OK: 40662306a36Sopenharmony_ci return "Valid resistance"; 40762306a36Sopenharmony_ci case PORT_DETECT_RESISTANCE_HIGH: 40862306a36Sopenharmony_ci return "Too high resistance"; 40962306a36Sopenharmony_ci case PORT_DETECT_OPEN_CIRCUIT: 41062306a36Sopenharmony_ci return "Open circuit"; 41162306a36Sopenharmony_ci case PORT_DETECT_MOSFET_FAULT: 41262306a36Sopenharmony_ci return "MOSFET fault"; 41362306a36Sopenharmony_ci case PORT_DETECT_LEGACY: 41462306a36Sopenharmony_ci return "Legacy device"; 41562306a36Sopenharmony_ci case PORT_DETECT_CAPACITANCE_INVALID_BEYOND: 41662306a36Sopenharmony_ci return "Invalid capacitance, beyond clamp voltage"; 41762306a36Sopenharmony_ci case PORT_DETECT_CAPACITANCE_INVALID_DELTA: 41862306a36Sopenharmony_ci return "Invalid capacitance, insufficient voltage delta"; 41962306a36Sopenharmony_ci case PORT_DETECT_CAPACITANCE_OUT_OF_RANGE: 42062306a36Sopenharmony_ci return "Valid capacitance, outside of legacy range"; 42162306a36Sopenharmony_ci case PORT_DETECT_RESERVED: 42262306a36Sopenharmony_ci case PORT_DETECT_RESERVED_2: 42362306a36Sopenharmony_ci default: 42462306a36Sopenharmony_ci return "Invalid"; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic char *port_class_status_string(uint8_t status_reg) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci switch (FIELD_GET(PORT_STATUS_CLASS_MASK, status_reg)) { 43162306a36Sopenharmony_ci case PORT_CLASS_UNKNOWN: 43262306a36Sopenharmony_ci return "Unknown"; 43362306a36Sopenharmony_ci case PORT_CLASS_RESERVED: 43462306a36Sopenharmony_ci case PORT_CLASS_0: 43562306a36Sopenharmony_ci return "0"; 43662306a36Sopenharmony_ci case PORT_CLASS_1: 43762306a36Sopenharmony_ci return "1"; 43862306a36Sopenharmony_ci case PORT_CLASS_2: 43962306a36Sopenharmony_ci return "2"; 44062306a36Sopenharmony_ci case PORT_CLASS_3: 44162306a36Sopenharmony_ci return "3"; 44262306a36Sopenharmony_ci case PORT_CLASS_4: 44362306a36Sopenharmony_ci return "4"; 44462306a36Sopenharmony_ci case PORT_CLASS_OVERCURRENT: 44562306a36Sopenharmony_ci return "Overcurrent"; 44662306a36Sopenharmony_ci case PORT_CLASS_MISMATCH: 44762306a36Sopenharmony_ci return "Mismatch"; 44862306a36Sopenharmony_ci default: 44962306a36Sopenharmony_ci return "Invalid"; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic char *port_poe_plus_status_string(uint8_t poe_plus, unsigned int port) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci return (BIT(port + 4) & poe_plus) ? "Yes" : "No"; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int tps23861_port_resistance(struct tps23861_data *data, int port) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci unsigned int raw_val; 46162306a36Sopenharmony_ci __le16 regval; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci regmap_bulk_read(data->regmap, 46462306a36Sopenharmony_ci PORT_1_RESISTANCE_LSB + PORT_N_RESISTANCE_LSB_OFFSET * port, 46562306a36Sopenharmony_ci ®val, 46662306a36Sopenharmony_ci 2); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci raw_val = le16_to_cpu(regval); 46962306a36Sopenharmony_ci switch (FIELD_GET(PORT_RESISTANCE_RSN_MASK, raw_val)) { 47062306a36Sopenharmony_ci case PORT_RESISTANCE_RSN_OTHER: 47162306a36Sopenharmony_ci return (FIELD_GET(PORT_RESISTANCE_MASK, raw_val) * RESISTANCE_LSB) / 10000; 47262306a36Sopenharmony_ci case PORT_RESISTANCE_RSN_LOW: 47362306a36Sopenharmony_ci return (FIELD_GET(PORT_RESISTANCE_MASK, raw_val) * RESISTANCE_LSB_LOW) / 10000; 47462306a36Sopenharmony_ci case PORT_RESISTANCE_RSN_SHORT: 47562306a36Sopenharmony_ci case PORT_RESISTANCE_RSN_OPEN: 47662306a36Sopenharmony_ci default: 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int tps23861_port_status_show(struct seq_file *s, void *data) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct tps23861_data *priv = s->private; 48462306a36Sopenharmony_ci unsigned int i, mode, poe_plus, status; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci regmap_read(priv->regmap, OPERATING_MODE, &mode); 48762306a36Sopenharmony_ci regmap_read(priv->regmap, POE_PLUS, &poe_plus); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci for (i = 0; i < TPS23861_NUM_PORTS; i++) { 49062306a36Sopenharmony_ci regmap_read(priv->regmap, PORT_1_STATUS + i, &status); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci seq_printf(s, "Port: \t\t%d\n", i + 1); 49362306a36Sopenharmony_ci seq_printf(s, "Operating mode: %s\n", port_operating_mode_string(mode, i)); 49462306a36Sopenharmony_ci seq_printf(s, "Detected: \t%s\n", port_detect_status_string(status)); 49562306a36Sopenharmony_ci seq_printf(s, "Class: \t\t%s\n", port_class_status_string(status)); 49662306a36Sopenharmony_ci seq_printf(s, "PoE Plus: \t%s\n", port_poe_plus_status_string(poe_plus, i)); 49762306a36Sopenharmony_ci seq_printf(s, "Resistance: \t%d\n", tps23861_port_resistance(priv, i)); 49862306a36Sopenharmony_ci seq_putc(s, '\n'); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return 0; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(tps23861_port_status); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic void tps23861_init_debugfs(struct tps23861_data *data, 50762306a36Sopenharmony_ci struct device *hwmon_dev) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci const char *debugfs_name; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci debugfs_name = devm_kasprintf(&data->client->dev, GFP_KERNEL, "%s-%s", 51262306a36Sopenharmony_ci data->client->name, dev_name(hwmon_dev)); 51362306a36Sopenharmony_ci if (!debugfs_name) 51462306a36Sopenharmony_ci return; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci data->debugfs_dir = debugfs_create_dir(debugfs_name, NULL); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci debugfs_create_file("port_status", 51962306a36Sopenharmony_ci 0400, 52062306a36Sopenharmony_ci data->debugfs_dir, 52162306a36Sopenharmony_ci data, 52262306a36Sopenharmony_ci &tps23861_port_status_fops); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic int tps23861_probe(struct i2c_client *client) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct device *dev = &client->dev; 52862306a36Sopenharmony_ci struct tps23861_data *data; 52962306a36Sopenharmony_ci struct device *hwmon_dev; 53062306a36Sopenharmony_ci u32 shunt_resistor; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 53362306a36Sopenharmony_ci if (!data) 53462306a36Sopenharmony_ci return -ENOMEM; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci data->client = client; 53762306a36Sopenharmony_ci i2c_set_clientdata(client, data); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci data->regmap = devm_regmap_init_i2c(client, &tps23861_regmap_config); 54062306a36Sopenharmony_ci if (IS_ERR(data->regmap)) { 54162306a36Sopenharmony_ci dev_err(dev, "failed to allocate register map\n"); 54262306a36Sopenharmony_ci return PTR_ERR(data->regmap); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "shunt-resistor-micro-ohms", &shunt_resistor)) 54662306a36Sopenharmony_ci data->shunt_resistor = shunt_resistor; 54762306a36Sopenharmony_ci else 54862306a36Sopenharmony_ci data->shunt_resistor = SHUNT_RESISTOR_DEFAULT; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT) 55162306a36Sopenharmony_ci regmap_clear_bits(data->regmap, 55262306a36Sopenharmony_ci TPS23861_GENERAL_MASK_1, 55362306a36Sopenharmony_ci TPS23861_CURRENT_SHUNT_MASK); 55462306a36Sopenharmony_ci else 55562306a36Sopenharmony_ci regmap_set_bits(data->regmap, 55662306a36Sopenharmony_ci TPS23861_GENERAL_MASK_1, 55762306a36Sopenharmony_ci TPS23861_CURRENT_SHUNT_MASK); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, 56062306a36Sopenharmony_ci data, &tps23861_chip_info, 56162306a36Sopenharmony_ci NULL); 56262306a36Sopenharmony_ci if (IS_ERR(hwmon_dev)) 56362306a36Sopenharmony_ci return PTR_ERR(hwmon_dev); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci tps23861_init_debugfs(data, hwmon_dev); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic void tps23861_remove(struct i2c_client *client) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct tps23861_data *data = i2c_get_clientdata(client); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci debugfs_remove_recursive(data->debugfs_dir); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused tps23861_of_match[] = { 57862306a36Sopenharmony_ci { .compatible = "ti,tps23861", }, 57962306a36Sopenharmony_ci { }, 58062306a36Sopenharmony_ci}; 58162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tps23861_of_match); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic struct i2c_driver tps23861_driver = { 58462306a36Sopenharmony_ci .probe = tps23861_probe, 58562306a36Sopenharmony_ci .remove = tps23861_remove, 58662306a36Sopenharmony_ci .driver = { 58762306a36Sopenharmony_ci .name = "tps23861", 58862306a36Sopenharmony_ci .of_match_table = of_match_ptr(tps23861_of_match), 58962306a36Sopenharmony_ci }, 59062306a36Sopenharmony_ci}; 59162306a36Sopenharmony_cimodule_i2c_driver(tps23861_driver); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 59462306a36Sopenharmony_ciMODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>"); 59562306a36Sopenharmony_ciMODULE_DESCRIPTION("TI TPS23861 PoE PSE"); 596