diff options
author | Alexandru Gagniuc <mr.nuke.me@gmail.com> | 2022-07-19 21:53:41 -0500 |
---|---|---|
committer | Sander Vanheule <sander@svanheule.net> | 2022-08-13 19:59:15 +0200 |
commit | c5fbd49d3fa6cdb10c635c74db91ca61177f9b70 (patch) | |
tree | 7950f80b9775a98ab7eee90b03286d686d8b77a6 | |
parent | 7bba7ccde9cb021e24ec83f1332aafd5898e23ef (diff) | |
download | upstream-c5fbd49d3fa6cdb10c635c74db91ca61177f9b70.tar.gz upstream-c5fbd49d3fa6cdb10c635c74db91ca61177f9b70.tar.bz2 upstream-c5fbd49d3fa6cdb10c635c74db91ca61177f9b70.zip |
kernel: add upstream patches for tps23861 PoE controller
These patches support the tps23861 PoE controller found on a number of
managed switches. The TPS23861 is an I2C-based quad IEEE 802.3at (PoE+)
Power-over-Ethernet PSE controller. It's also found on some Realtek
based switches, where we expect the bulk of the users to reside.
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
[Disable driver in generic/config-5.10]
Signed-off-by: Sander Vanheule <sander@svanheule.net>
6 files changed, 898 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.10/871-v5.12-hwmon-add-Texas-Instruments-TPS23861-driver.patch b/target/linux/generic/backport-5.10/871-v5.12-hwmon-add-Texas-Instruments-TPS23861-driver.patch new file mode 100644 index 0000000000..3c3430a2f7 --- /dev/null +++ b/target/linux/generic/backport-5.10/871-v5.12-hwmon-add-Texas-Instruments-TPS23861-driver.patch @@ -0,0 +1,711 @@ +From 97c95dbbba64dbd6e98e033e396695f328033966 Mon Sep 17 00:00:00 2001 +From: Robert Marko <robert.marko@sartura.hr> +Date: Thu, 21 Jan 2021 14:44:33 +0100 +Subject: [PATCH 1/4] hwmon: add Texas Instruments TPS23861 driver + +Add basic monitoring support as well as port on/off control for Texas +Instruments TPS23861 PoE PSE IC. + +Signed-off-by: Robert Marko <robert.marko@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Reviewed-by: Guenter Roeck <linux@roeck-us.net> +Link: https://lore.kernel.org/r/20210121134434.2782405-2-robert.marko@sartura.hr +Signed-off-by: Guenter Roeck <linux@roeck-us.net> +--- + Documentation/hwmon/index.rst | 1 + + Documentation/hwmon/tps23861.rst | 41 +++ + drivers/hwmon/Kconfig | 11 + + drivers/hwmon/Makefile | 1 + + drivers/hwmon/tps23861.c | 601 +++++++++++++++++++++++++++++++ + 5 files changed, 655 insertions(+) + create mode 100644 Documentation/hwmon/tps23861.rst + create mode 100644 drivers/hwmon/tps23861.c + +--- a/Documentation/hwmon/index.rst ++++ b/Documentation/hwmon/index.rst +@@ -172,6 +172,7 @@ Hardware Monitoring Kernel Drivers + tmp401 + tmp421 + tmp513 ++ tps23861 + tps40422 + tps53679 + twl4030-madc-hwmon +--- /dev/null ++++ b/Documentation/hwmon/tps23861.rst +@@ -0,0 +1,41 @@ ++.. SPDX-License-Identifier: GPL-2.0-only ++ ++Kernel driver tps23861 ++====================== ++ ++Supported chips: ++ * Texas Instruments TPS23861 ++ ++ Prefix: 'tps23861' ++ ++ Datasheet: https://www.ti.com/lit/gpn/tps23861 ++ ++Author: Robert Marko <robert.marko@sartura.hr> ++ ++Description ++----------- ++ ++This driver supports hardware monitoring for Texas Instruments TPS23861 PoE PSE. ++ ++TPS23861 is a quad port IEEE802.3at PSE controller with optional I2C control ++and monitoring capabilities. ++ ++TPS23861 offers three modes of operation: Auto, Semi-Auto and Manual. ++ ++This driver only supports the Auto mode of operation providing monitoring ++as well as enabling/disabling the four ports. ++ ++Sysfs entries ++------------- ++ ++======================= ===================================================================== ++in[0-3]_input Voltage on ports [1-4] ++in[0-3]_label "Port[1-4]" ++in4_input IC input voltage ++in4_label "Input" ++temp1_input IC die temperature ++temp1_label "Die" ++curr[1-4]_input Current on ports [1-4] ++in[1-4]_label "Port[1-4]" ++in[0-3]_enable Enable/disable ports [1-4] ++======================= ===================================================================== +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -1102,6 +1102,17 @@ config SENSORS_TC654 + This driver can also be built as a module. If so, the module + will be called tc654. + ++config SENSORS_TPS23861 ++ tristate "Texas Instruments TPS23861 PoE PSE" ++ depends on I2C ++ select REGMAP_I2C ++ help ++ If you say yes here you get support for Texas Instruments ++ TPS23861 802.3at PoE PSE chips. ++ ++ This driver can also be built as a module. If so, the module ++ will be called tps23861. ++ + config SENSORS_MENF21BMC_HWMON + tristate "MEN 14F021P00 BMC Hardware Monitoring" + depends on MFD_MENF21BMC +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -141,6 +141,7 @@ obj-$(CONFIG_SENSORS_MAX31790) += max317 + obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o + obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o + obj-$(CONFIG_SENSORS_TC654) += tc654.o ++obj-$(CONFIG_SENSORS_TPS23861) += tps23861.o + obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o + obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o + obj-$(CONFIG_SENSORS_MR75203) += mr75203.o +--- /dev/null ++++ b/drivers/hwmon/tps23861.c +@@ -0,0 +1,601 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2020 Sartura Ltd. ++ * ++ * Driver for the TI TPS23861 PoE PSE. ++ * ++ * Author: Robert Marko <robert.marko@sartura.hr> ++ */ ++ ++#include <linux/bitfield.h> ++#include <linux/debugfs.h> ++#include <linux/delay.h> ++#include <linux/hwmon-sysfs.h> ++#include <linux/hwmon.h> ++#include <linux/i2c.h> ++#include <linux/module.h> ++#include <linux/of_device.h> ++#include <linux/regmap.h> ++ ++#define TEMPERATURE 0x2c ++#define INPUT_VOLTAGE_LSB 0x2e ++#define INPUT_VOLTAGE_MSB 0x2f ++#define PORT_1_CURRENT_LSB 0x30 ++#define PORT_1_CURRENT_MSB 0x31 ++#define PORT_1_VOLTAGE_LSB 0x32 ++#define PORT_1_VOLTAGE_MSB 0x33 ++#define PORT_2_CURRENT_LSB 0x34 ++#define PORT_2_CURRENT_MSB 0x35 ++#define PORT_2_VOLTAGE_LSB 0x36 ++#define PORT_2_VOLTAGE_MSB 0x37 ++#define PORT_3_CURRENT_LSB 0x38 ++#define PORT_3_CURRENT_MSB 0x39 ++#define PORT_3_VOLTAGE_LSB 0x3a ++#define PORT_3_VOLTAGE_MSB 0x3b ++#define PORT_4_CURRENT_LSB 0x3c ++#define PORT_4_CURRENT_MSB 0x3d ++#define PORT_4_VOLTAGE_LSB 0x3e ++#define PORT_4_VOLTAGE_MSB 0x3f ++#define PORT_N_CURRENT_LSB_OFFSET 0x04 ++#define PORT_N_VOLTAGE_LSB_OFFSET 0x04 ++#define VOLTAGE_CURRENT_MASK GENMASK(13, 0) ++#define PORT_1_RESISTANCE_LSB 0x60 ++#define PORT_1_RESISTANCE_MSB 0x61 ++#define PORT_2_RESISTANCE_LSB 0x62 ++#define PORT_2_RESISTANCE_MSB 0x63 ++#define PORT_3_RESISTANCE_LSB 0x64 ++#define PORT_3_RESISTANCE_MSB 0x65 ++#define PORT_4_RESISTANCE_LSB 0x66 ++#define PORT_4_RESISTANCE_MSB 0x67 ++#define PORT_N_RESISTANCE_LSB_OFFSET 0x02 ++#define PORT_RESISTANCE_MASK GENMASK(13, 0) ++#define PORT_RESISTANCE_RSN_MASK GENMASK(15, 14) ++#define PORT_RESISTANCE_RSN_OTHER 0 ++#define PORT_RESISTANCE_RSN_LOW 1 ++#define PORT_RESISTANCE_RSN_OPEN 2 ++#define PORT_RESISTANCE_RSN_SHORT 3 ++#define PORT_1_STATUS 0x0c ++#define PORT_2_STATUS 0x0d ++#define PORT_3_STATUS 0x0e ++#define PORT_4_STATUS 0x0f ++#define PORT_STATUS_CLASS_MASK GENMASK(7, 4) ++#define PORT_STATUS_DETECT_MASK GENMASK(3, 0) ++#define PORT_CLASS_UNKNOWN 0 ++#define PORT_CLASS_1 1 ++#define PORT_CLASS_2 2 ++#define PORT_CLASS_3 3 ++#define PORT_CLASS_4 4 ++#define PORT_CLASS_RESERVED 5 ++#define PORT_CLASS_0 6 ++#define PORT_CLASS_OVERCURRENT 7 ++#define PORT_CLASS_MISMATCH 8 ++#define PORT_DETECT_UNKNOWN 0 ++#define PORT_DETECT_SHORT 1 ++#define PORT_DETECT_RESERVED 2 ++#define PORT_DETECT_RESISTANCE_LOW 3 ++#define PORT_DETECT_RESISTANCE_OK 4 ++#define PORT_DETECT_RESISTANCE_HIGH 5 ++#define PORT_DETECT_OPEN_CIRCUIT 6 ++#define PORT_DETECT_RESERVED_2 7 ++#define PORT_DETECT_MOSFET_FAULT 8 ++#define PORT_DETECT_LEGACY 9 ++/* Measurment beyond clamp voltage */ ++#define PORT_DETECT_CAPACITANCE_INVALID_BEYOND 10 ++/* Insufficient voltage delta */ ++#define PORT_DETECT_CAPACITANCE_INVALID_DELTA 11 ++#define PORT_DETECT_CAPACITANCE_OUT_OF_RANGE 12 ++#define POE_PLUS 0x40 ++#define OPERATING_MODE 0x12 ++#define OPERATING_MODE_OFF 0 ++#define OPERATING_MODE_MANUAL 1 ++#define OPERATING_MODE_SEMI 2 ++#define OPERATING_MODE_AUTO 3 ++#define OPERATING_MODE_PORT_1_MASK GENMASK(1, 0) ++#define OPERATING_MODE_PORT_2_MASK GENMASK(3, 2) ++#define OPERATING_MODE_PORT_3_MASK GENMASK(5, 4) ++#define OPERATING_MODE_PORT_4_MASK GENMASK(7, 6) ++ ++#define DETECT_CLASS_RESTART 0x18 ++#define POWER_ENABLE 0x19 ++#define TPS23861_NUM_PORTS 4 ++ ++#define TEMPERATURE_LSB 652 /* 0.652 degrees Celsius */ ++#define VOLTAGE_LSB 3662 /* 3.662 mV */ ++#define SHUNT_RESISTOR_DEFAULT 255000 /* 255 mOhm */ ++#define CURRENT_LSB_255 62260 /* 62.260 uA */ ++#define CURRENT_LSB_250 61039 /* 61.039 uA */ ++#define RESISTANCE_LSB 110966 /* 11.0966 Ohm*/ ++#define RESISTANCE_LSB_LOW 157216 /* 15.7216 Ohm*/ ++ ++struct tps23861_data { ++ struct regmap *regmap; ++ u32 shunt_resistor; ++ struct i2c_client *client; ++ struct dentry *debugfs_dir; ++}; ++ ++static struct regmap_config tps23861_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++}; ++ ++static int tps23861_read_temp(struct tps23861_data *data, long *val) ++{ ++ unsigned int regval; ++ int err; ++ ++ err = regmap_read(data->regmap, TEMPERATURE, ®val); ++ if (err < 0) ++ return err; ++ ++ *val = (regval * TEMPERATURE_LSB) - 20000; ++ ++ return 0; ++} ++ ++static int tps23861_read_voltage(struct tps23861_data *data, int channel, ++ long *val) ++{ ++ unsigned int regval; ++ int err; ++ ++ if (channel < TPS23861_NUM_PORTS) { ++ err = regmap_bulk_read(data->regmap, ++ PORT_1_VOLTAGE_LSB + channel * PORT_N_VOLTAGE_LSB_OFFSET, ++ ®val, 2); ++ } else { ++ err = regmap_bulk_read(data->regmap, ++ INPUT_VOLTAGE_LSB, ++ ®val, 2); ++ } ++ if (err < 0) ++ return err; ++ ++ *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * VOLTAGE_LSB) / 1000; ++ ++ return 0; ++} ++ ++static int tps23861_read_current(struct tps23861_data *data, int channel, ++ long *val) ++{ ++ unsigned int current_lsb; ++ unsigned int regval; ++ int err; ++ ++ if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT) ++ current_lsb = CURRENT_LSB_255; ++ else ++ current_lsb = CURRENT_LSB_250; ++ ++ err = regmap_bulk_read(data->regmap, ++ PORT_1_CURRENT_LSB + channel * PORT_N_CURRENT_LSB_OFFSET, ++ ®val, 2); ++ if (err < 0) ++ return err; ++ ++ *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * current_lsb) / 1000000; ++ ++ return 0; ++} ++ ++static int tps23861_port_disable(struct tps23861_data *data, int channel) ++{ ++ unsigned int regval = 0; ++ int err; ++ ++ regval |= BIT(channel + 4); ++ err = regmap_write(data->regmap, POWER_ENABLE, regval); ++ ++ return err; ++} ++ ++static int tps23861_port_enable(struct tps23861_data *data, int channel) ++{ ++ unsigned int regval = 0; ++ int err; ++ ++ regval |= BIT(channel); ++ regval |= BIT(channel + 4); ++ err = regmap_write(data->regmap, DETECT_CLASS_RESTART, regval); ++ ++ return err; ++} ++ ++static umode_t tps23861_is_visible(const void *data, enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ switch (type) { ++ case hwmon_temp: ++ switch (attr) { ++ case hwmon_temp_input: ++ case hwmon_temp_label: ++ return 0444; ++ default: ++ return 0; ++ } ++ case hwmon_in: ++ switch (attr) { ++ case hwmon_in_input: ++ case hwmon_in_label: ++ return 0444; ++ case hwmon_in_enable: ++ return 0200; ++ default: ++ return 0; ++ } ++ case hwmon_curr: ++ switch (attr) { ++ case hwmon_curr_input: ++ case hwmon_curr_label: ++ return 0444; ++ default: ++ return 0; ++ } ++ default: ++ return 0; ++ } ++} ++ ++static int tps23861_write(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long val) ++{ ++ struct tps23861_data *data = dev_get_drvdata(dev); ++ int err; ++ ++ switch (type) { ++ case hwmon_in: ++ switch (attr) { ++ case hwmon_in_enable: ++ if (val == 0) ++ err = tps23861_port_disable(data, channel); ++ else if (val == 1) ++ err = tps23861_port_enable(data, channel); ++ else ++ err = -EINVAL; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return err; ++} ++ ++static int tps23861_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ struct tps23861_data *data = dev_get_drvdata(dev); ++ int err; ++ ++ switch (type) { ++ case hwmon_temp: ++ switch (attr) { ++ case hwmon_temp_input: ++ err = tps23861_read_temp(data, val); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ break; ++ case hwmon_in: ++ switch (attr) { ++ case hwmon_in_input: ++ err = tps23861_read_voltage(data, channel, val); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ break; ++ case hwmon_curr: ++ switch (attr) { ++ case hwmon_curr_input: ++ err = tps23861_read_current(data, channel, val); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return err; ++} ++ ++static const char * const tps23861_port_label[] = { ++ "Port1", ++ "Port2", ++ "Port3", ++ "Port4", ++ "Input", ++}; ++ ++static int tps23861_read_string(struct device *dev, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel, const char **str) ++{ ++ switch (type) { ++ case hwmon_in: ++ case hwmon_curr: ++ *str = tps23861_port_label[channel]; ++ break; ++ case hwmon_temp: ++ *str = "Die"; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static const struct hwmon_channel_info *tps23861_info[] = { ++ HWMON_CHANNEL_INFO(chip, ++ HWMON_C_REGISTER_TZ), ++ HWMON_CHANNEL_INFO(temp, ++ HWMON_T_INPUT | HWMON_T_LABEL), ++ HWMON_CHANNEL_INFO(in, ++ HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, ++ HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, ++ HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, ++ HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, ++ HWMON_I_INPUT | HWMON_I_LABEL), ++ HWMON_CHANNEL_INFO(curr, ++ HWMON_C_INPUT | HWMON_C_LABEL, ++ HWMON_C_INPUT | HWMON_C_LABEL, ++ HWMON_C_INPUT | HWMON_C_LABEL, ++ HWMON_C_INPUT | HWMON_C_LABEL), ++ NULL ++}; ++ ++static const struct hwmon_ops tps23861_hwmon_ops = { ++ .is_visible = tps23861_is_visible, ++ .write = tps23861_write, ++ .read = tps23861_read, ++ .read_string = tps23861_read_string, ++}; ++ ++static const struct hwmon_chip_info tps23861_chip_info = { ++ .ops = &tps23861_hwmon_ops, ++ .info = tps23861_info, ++}; ++ ++static char *tps23861_port_operating_mode(struct tps23861_data *data, int port) ++{ ++ unsigned int regval; ++ int mode; ++ ++ regmap_read(data->regmap, OPERATING_MODE, ®val); ++ ++ switch (port) { ++ case 1: ++ mode = FIELD_GET(OPERATING_MODE_PORT_1_MASK, regval); ++ break; ++ case 2: ++ mode = FIELD_GET(OPERATING_MODE_PORT_2_MASK, regval); ++ break; ++ case 3: ++ mode = FIELD_GET(OPERATING_MODE_PORT_3_MASK, regval); ++ break; ++ case 4: ++ mode = FIELD_GET(OPERATING_MODE_PORT_4_MASK, regval); ++ break; ++ default: ++ mode = -EINVAL; ++ } ++ ++ switch (mode) { ++ case OPERATING_MODE_OFF: ++ return "Off"; ++ case OPERATING_MODE_MANUAL: ++ return "Manual"; ++ case OPERATING_MODE_SEMI: ++ return "Semi-Auto"; ++ case OPERATING_MODE_AUTO: ++ return "Auto"; ++ default: ++ return "Invalid"; ++ } ++} ++ ++static char *tps23861_port_detect_status(struct tps23861_data *data, int port) ++{ ++ unsigned int regval; ++ ++ regmap_read(data->regmap, ++ PORT_1_STATUS + (port - 1), ++ ®val); ++ ++ switch (FIELD_GET(PORT_STATUS_DETECT_MASK, regval)) { ++ case PORT_DETECT_UNKNOWN: ++ return "Unknown device"; ++ case PORT_DETECT_SHORT: ++ return "Short circuit"; ++ case PORT_DETECT_RESISTANCE_LOW: ++ return "Too low resistance"; ++ case PORT_DETECT_RESISTANCE_OK: ++ return "Valid resistance"; ++ case PORT_DETECT_RESISTANCE_HIGH: ++ return "Too high resistance"; ++ case PORT_DETECT_OPEN_CIRCUIT: ++ return "Open circuit"; ++ case PORT_DETECT_MOSFET_FAULT: ++ return "MOSFET fault"; ++ case PORT_DETECT_LEGACY: ++ return "Legacy device"; ++ case PORT_DETECT_CAPACITANCE_INVALID_BEYOND: ++ return "Invalid capacitance, beyond clamp voltage"; ++ case PORT_DETECT_CAPACITANCE_INVALID_DELTA: ++ return "Invalid capacitance, insufficient voltage delta"; ++ case PORT_DETECT_CAPACITANCE_OUT_OF_RANGE: ++ return "Valid capacitance, outside of legacy range"; ++ case PORT_DETECT_RESERVED: ++ case PORT_DETECT_RESERVED_2: ++ default: ++ return "Invalid"; ++ } ++} ++ ++static char *tps23861_port_class_status(struct tps23861_data *data, int port) ++{ ++ unsigned int regval; ++ ++ regmap_read(data->regmap, ++ PORT_1_STATUS + (port - 1), ++ ®val); ++ ++ switch (FIELD_GET(PORT_STATUS_CLASS_MASK, regval)) { ++ case PORT_CLASS_UNKNOWN: ++ return "Unknown"; ++ case PORT_CLASS_RESERVED: ++ case PORT_CLASS_0: ++ return "0"; ++ case PORT_CLASS_1: ++ return "1"; ++ case PORT_CLASS_2: ++ return "2"; ++ case PORT_CLASS_3: ++ return "3"; ++ case PORT_CLASS_4: ++ return "4"; ++ case PORT_CLASS_OVERCURRENT: ++ return "Overcurrent"; ++ case PORT_CLASS_MISMATCH: ++ return "Mismatch"; ++ default: ++ return "Invalid"; ++ } ++} ++ ++static char *tps23861_port_poe_plus_status(struct tps23861_data *data, int port) ++{ ++ unsigned int regval; ++ ++ regmap_read(data->regmap, POE_PLUS, ®val); ++ ++ if (BIT(port + 3) & regval) ++ return "Yes"; ++ else ++ return "No"; ++} ++ ++static int tps23861_port_resistance(struct tps23861_data *data, int port) ++{ ++ u16 regval; ++ ++ regmap_bulk_read(data->regmap, ++ PORT_1_RESISTANCE_LSB + PORT_N_RESISTANCE_LSB_OFFSET * (port - 1), ++ ®val, ++ 2); ++ ++ switch (FIELD_GET(PORT_RESISTANCE_RSN_MASK, regval)) { ++ case PORT_RESISTANCE_RSN_OTHER: ++ return (FIELD_GET(PORT_RESISTANCE_MASK, regval) * RESISTANCE_LSB) / 10000; ++ case PORT_RESISTANCE_RSN_LOW: ++ return (FIELD_GET(PORT_RESISTANCE_MASK, regval) * RESISTANCE_LSB_LOW) / 10000; ++ case PORT_RESISTANCE_RSN_SHORT: ++ case PORT_RESISTANCE_RSN_OPEN: ++ default: ++ return 0; ++ } ++} ++ ++static int tps23861_port_status_show(struct seq_file *s, void *data) ++{ ++ struct tps23861_data *priv = s->private; ++ int i; ++ ++ for (i = 1; i < TPS23861_NUM_PORTS + 1; i++) { ++ seq_printf(s, "Port: \t\t%d\n", i); ++ seq_printf(s, "Operating mode: %s\n", tps23861_port_operating_mode(priv, i)); ++ seq_printf(s, "Detected: \t%s\n", tps23861_port_detect_status(priv, i)); ++ seq_printf(s, "Class: \t\t%s\n", tps23861_port_class_status(priv, i)); ++ seq_printf(s, "PoE Plus: \t%s\n", tps23861_port_poe_plus_status(priv, i)); ++ seq_printf(s, "Resistance: \t%d\n", tps23861_port_resistance(priv, i)); ++ seq_putc(s, '\n'); ++ } ++ ++ return 0; ++} ++ ++DEFINE_SHOW_ATTRIBUTE(tps23861_port_status); ++ ++static void tps23861_init_debugfs(struct tps23861_data *data) ++{ ++ data->debugfs_dir = debugfs_create_dir(data->client->name, NULL); ++ ++ debugfs_create_file("port_status", ++ 0400, ++ data->debugfs_dir, ++ data, ++ &tps23861_port_status_fops); ++} ++ ++static int tps23861_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct tps23861_data *data; ++ struct device *hwmon_dev; ++ u32 shunt_resistor; ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->client = client; ++ i2c_set_clientdata(client, data); ++ ++ data->regmap = devm_regmap_init_i2c(client, &tps23861_regmap_config); ++ if (IS_ERR(data->regmap)) { ++ dev_err(dev, "failed to allocate register map\n"); ++ return PTR_ERR(data->regmap); ++ } ++ ++ if (!of_property_read_u32(dev->of_node, "shunt-resistor-micro-ohms", &shunt_resistor)) ++ data->shunt_resistor = shunt_resistor; ++ else ++ data->shunt_resistor = SHUNT_RESISTOR_DEFAULT; ++ ++ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ++ data, &tps23861_chip_info, ++ NULL); ++ if (IS_ERR(hwmon_dev)) ++ return PTR_ERR(hwmon_dev); ++ ++ tps23861_init_debugfs(data); ++ ++ return 0; ++} ++ ++static int tps23861_remove(struct i2c_client *client) ++{ ++ struct tps23861_data *data = i2c_get_clientdata(client); ++ ++ debugfs_remove_recursive(data->debugfs_dir); ++ ++ return 0; ++} ++ ++static const struct of_device_id __maybe_unused tps23861_of_match[] = { ++ { .compatible = "ti,tps23861", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, tps23861_of_match); ++ ++static struct i2c_driver tps23861_driver = { ++ .probe_new = tps23861_probe, ++ .remove = tps23861_remove, ++ .driver = { ++ .name = "tps23861", ++ .of_match_table = of_match_ptr(tps23861_of_match), ++ }, ++}; ++module_i2c_driver(tps23861_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>"); ++MODULE_DESCRIPTION("TI TPS23861 PoE PSE"); diff --git a/target/linux/generic/backport-5.10/872-v5.13-01-hwmon-tps23861-define-regmap-max-register.patch b/target/linux/generic/backport-5.10/872-v5.13-01-hwmon-tps23861-define-regmap-max-register.patch new file mode 100644 index 0000000000..f7ed386944 --- /dev/null +++ b/target/linux/generic/backport-5.10/872-v5.13-01-hwmon-tps23861-define-regmap-max-register.patch @@ -0,0 +1,29 @@ +From 3d61a7b3a714eb3ef1777e3c576576aca2b85365 Mon Sep 17 00:00:00 2001 +From: Robert Marko <robert.marko@sartura.hr> +Date: Thu, 10 Jun 2021 00:07:26 +0200 +Subject: [PATCH 2/4] hwmon: (tps23861) define regmap max register + +Define the max register address the device supports. +This allows reading the whole register space via +regmap debugfs, without it only register 0x0 is visible. + +This was forgotten in the original driver commit. + +Fixes: fff7b8ab2255 ("hwmon: add Texas Instruments TPS23861 driver") +Signed-off-by: Robert Marko <robert.marko@sartura.hr> +Link: https://lore.kernel.org/r/20210609220728.499879-1-robert.marko@sartura.hr +Signed-off-by: Guenter Roeck <linux@roeck-us.net> +--- + drivers/hwmon/tps23861.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/hwmon/tps23861.c ++++ b/drivers/hwmon/tps23861.c +@@ -117,6 +117,7 @@ struct tps23861_data { + static struct regmap_config tps23861_regmap_config = { + .reg_bits = 8, + .val_bits = 8, ++ .max_register = 0x6f, + }; + + static int tps23861_read_temp(struct tps23861_data *data, long *val) diff --git a/target/linux/generic/backport-5.10/872-v5.13-02-hwmon-tps23861-set-current-shunt-value.patch b/target/linux/generic/backport-5.10/872-v5.13-02-hwmon-tps23861-set-current-shunt-value.patch new file mode 100644 index 0000000000..f1051f77d5 --- /dev/null +++ b/target/linux/generic/backport-5.10/872-v5.13-02-hwmon-tps23861-set-current-shunt-value.patch @@ -0,0 +1,57 @@ +From 9bca598d4a86e88afb29fdb516c68b2519bd0fb9 Mon Sep 17 00:00:00 2001 +From: Robert Marko <robert.marko@sartura.hr> +Date: Thu, 10 Jun 2021 00:07:27 +0200 +Subject: [PATCH 3/4] hwmon: (tps23861) set current shunt value + +TPS23861 has a configuration bit for setting of the +current shunt value used on the board. +Its bit 0 of the General Mask 1 register. + +According to the datasheet bit values are: +0 for 255 mOhm (Default) +1 for 250 mOhm + +So, configure the bit before registering the hwmon +device according to the value passed in the DTS or +default one if none is passed. + +This caused potentially reading slightly skewed values +due to max current value being 1.02A when 250mOhm shunt +is used instead of 1.0A when 255mOhm is used. + +Fixes: fff7b8ab2255 ("hwmon: add Texas Instruments TPS23861 driver") +Signed-off-by: Robert Marko <robert.marko@sartura.hr> +Link: https://lore.kernel.org/r/20210609220728.499879-2-robert.marko@sartura.hr +Signed-off-by: Guenter Roeck <linux@roeck-us.net> +--- + drivers/hwmon/tps23861.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/drivers/hwmon/tps23861.c ++++ b/drivers/hwmon/tps23861.c +@@ -99,6 +99,9 @@ + #define POWER_ENABLE 0x19 + #define TPS23861_NUM_PORTS 4 + ++#define TPS23861_GENERAL_MASK_1 0x17 ++#define TPS23861_CURRENT_SHUNT_MASK BIT(0) ++ + #define TEMPERATURE_LSB 652 /* 0.652 degrees Celsius */ + #define VOLTAGE_LSB 3662 /* 3.662 mV */ + #define SHUNT_RESISTOR_DEFAULT 255000 /* 255 mOhm */ +@@ -561,6 +564,15 @@ static int tps23861_probe(struct i2c_cli + else + data->shunt_resistor = SHUNT_RESISTOR_DEFAULT; + ++ if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT) ++ regmap_clear_bits(data->regmap, ++ TPS23861_GENERAL_MASK_1, ++ TPS23861_CURRENT_SHUNT_MASK); ++ else ++ regmap_set_bits(data->regmap, ++ TPS23861_GENERAL_MASK_1, ++ TPS23861_CURRENT_SHUNT_MASK); ++ + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &tps23861_chip_info, + NULL); diff --git a/target/linux/generic/backport-5.10/872-v5.13-03-hwmon-tps23861-correct-shunt-LSB-values.patch b/target/linux/generic/backport-5.10/872-v5.13-03-hwmon-tps23861-correct-shunt-LSB-values.patch new file mode 100644 index 0000000000..2485d7a0ee --- /dev/null +++ b/target/linux/generic/backport-5.10/872-v5.13-03-hwmon-tps23861-correct-shunt-LSB-values.patch @@ -0,0 +1,34 @@ +From b447e689a26614ce08a431e8000e8a650a63dcb3 Mon Sep 17 00:00:00 2001 +From: Robert Marko <robert.marko@sartura.hr> +Date: Thu, 10 Jun 2021 00:07:28 +0200 +Subject: [PATCH 4/4] hwmon: (tps23861) correct shunt LSB values + +Current shunt LSB values got reversed during in the +original driver commit. + +So, correct the current shunt LSB values according to +the datasheet. + +This caused reading slightly skewed current values. + +Fixes: fff7b8ab2255 ("hwmon: add Texas Instruments TPS23861 driver") +Signed-off-by: Robert Marko <robert.marko@sartura.hr> +Link: https://lore.kernel.org/r/20210609220728.499879-3-robert.marko@sartura.hr +Signed-off-by: Guenter Roeck <linux@roeck-us.net> +--- + drivers/hwmon/tps23861.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/hwmon/tps23861.c ++++ b/drivers/hwmon/tps23861.c +@@ -105,8 +105,8 @@ + #define TEMPERATURE_LSB 652 /* 0.652 degrees Celsius */ + #define VOLTAGE_LSB 3662 /* 3.662 mV */ + #define SHUNT_RESISTOR_DEFAULT 255000 /* 255 mOhm */ +-#define CURRENT_LSB_255 62260 /* 62.260 uA */ +-#define CURRENT_LSB_250 61039 /* 61.039 uA */ ++#define CURRENT_LSB_250 62260 /* 62.260 uA */ ++#define CURRENT_LSB_255 61039 /* 61.039 uA */ + #define RESISTANCE_LSB 110966 /* 11.0966 Ohm*/ + #define RESISTANCE_LSB_LOW 157216 /* 15.7216 Ohm*/ + diff --git a/target/linux/generic/backport-5.10/873-v6.0-hwmon-tps23861-fix-byte-order-in-current-and-voltage.patch b/target/linux/generic/backport-5.10/873-v6.0-hwmon-tps23861-fix-byte-order-in-current-and-voltage.patch new file mode 100644 index 0000000000..45c2d0b4a4 --- /dev/null +++ b/target/linux/generic/backport-5.10/873-v6.0-hwmon-tps23861-fix-byte-order-in-current-and-voltage.patch @@ -0,0 +1,66 @@ +From 0eabb1396656f215a5333a9444158b17b0fd3247 Mon Sep 17 00:00:00 2001 +From: Alexandru Gagniuc <mr.nuke.me@gmail.com> +Date: Wed, 20 Jul 2022 22:22:55 -0500 +Subject: hwmon: (tps23861) fix byte order in current and voltage registers + +Trying to use this driver on a big-endian machine results in garbage +values for voltage and current. The tps23861 registers are little- +endian, and regmap_read_bulk() does not do byte order conversion. Thus +on BE machines, the most significant bytes got modified, and were +trimmed by the VOLTAGE_CURRENT_MASK. + +To resolve this use uint16_t values, and convert them to host byte +order using le16_to_cpu(). This results in correct readings on MIPS. + +Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com> +Link: https://lore.kernel.org/r/20220721032255.2850647-1-mr.nuke.me@gmail.com +[groeck: Use __le16 instead of uint16_t] +Signed-off-by: Guenter Roeck <linux@roeck-us.net> +--- + drivers/hwmon/tps23861.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +--- a/drivers/hwmon/tps23861.c ++++ b/drivers/hwmon/tps23861.c +@@ -140,7 +140,8 @@ static int tps23861_read_temp(struct tps + static int tps23861_read_voltage(struct tps23861_data *data, int channel, + long *val) + { +- unsigned int regval; ++ __le16 regval; ++ long raw_val; + int err; + + if (channel < TPS23861_NUM_PORTS) { +@@ -155,7 +156,8 @@ static int tps23861_read_voltage(struct + if (err < 0) + return err; + +- *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * VOLTAGE_LSB) / 1000; ++ raw_val = le16_to_cpu(regval); ++ *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * VOLTAGE_LSB) / 1000; + + return 0; + } +@@ -163,8 +165,9 @@ static int tps23861_read_voltage(struct + static int tps23861_read_current(struct tps23861_data *data, int channel, + long *val) + { +- unsigned int current_lsb; +- unsigned int regval; ++ long raw_val, current_lsb; ++ __le16 regval; ++ + int err; + + if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT) +@@ -178,7 +181,8 @@ static int tps23861_read_current(struct + if (err < 0) + return err; + +- *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * current_lsb) / 1000000; ++ raw_val = le16_to_cpu(regval); ++ *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * current_lsb) / 1000000; + + return 0; + } diff --git a/target/linux/generic/config-5.10 b/target/linux/generic/config-5.10 index 3d04a17243..b5f29cf547 100644 --- a/target/linux/generic/config-5.10 +++ b/target/linux/generic/config-5.10 @@ -5304,6 +5304,7 @@ CONFIG_SELECT_MEMORY_MODEL=y # CONFIG_SENSORS_TMP401 is not set # CONFIG_SENSORS_TMP421 is not set # CONFIG_SENSORS_TMP513 is not set +# CONFIG_SENSORS_TPS23861 is not set # CONFIG_SENSORS_TPS40422 is not set # CONFIG_SENSORS_TPS53679 is not set # CONFIG_SENSORS_TSL2550 is not set |