Adds support for Texas Instruments TPS2483 Series Hot Swap Voltage Controller. The driver in its current form supports reading Shunt Voltage, Bus Voltage, Power and Current. Since TPS2483 can be configured to a particular slave address based on the state of A0 and A1 pins, instantiation is done manually. For eg:- echo tps2483 0x40 > /sys/bus/i2c/devices/i2c-1/new_device Signed-off-by: Philby John <philby.j@xxxxxxx> --- drivers/hwmon/Kconfig | 11 +++ drivers/hwmon/Makefile | 1 + drivers/hwmon/tps2483.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 drivers/hwmon/tps2483.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 8dc28b26916e..9e51c25f695e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1852,6 +1852,17 @@ config SENSORS_TMP513 This driver can also be built as a module. If so, the module will be called tmp513. +config SENSORS_TPS2483 + tristate "Texas Instruments TPS2483" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for Texas Instruments TPS2483 + Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called tps2483. + config SENSORS_VEXPRESS tristate "Versatile Express" depends on VEXPRESS_CONFIG diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index a8f4b35b136b..2f1551c77a4c 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -179,6 +179,7 @@ obj-$(CONFIG_SENSORS_TMP108) += tmp108.o obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o obj-$(CONFIG_SENSORS_TMP513) += tmp513.o +obj-$(CONFIG_SENSORS_TPS2483) += tps2483.o obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress-hwmon.o obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o diff --git a/drivers/hwmon/tps2483.c b/drivers/hwmon/tps2483.c new file mode 100644 index 000000000000..957b6e3e0cd9 --- /dev/null +++ b/drivers/hwmon/tps2483.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Texas Instruments TPS 2483 Hot Swap controller + * + * Copyright (c) 2020 Philby John + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/regmap.h> + +/* chip registers */ +#define TPS2483_CONFIG 0x00 +#define TPS2483_SHUNT_VOLT 0x01 +#define TPS2483_BUS_VOLT 0x02 +#define TPS2483_PWR 0x03 +#define TPS2483_CURRENT 0x04 +#define TPS2483_CALIB 0x05 +#define TPS2483_MASK 0x06 +#define TPS2483_ALERT 0x07 + +#define MAX_CALIB_FACTOR 32757 +#define MIN_CALIB_FACTOR 0 + +/* Fault register bits */ +#define SHUNT_OV BIT(15) +#define SHUNT_UV BIT(14) +#define BUS_OV BIT(13) +#define BUS_UV BIT(12) +#define POWER_OL BIT(11) +#define MATH_OF BIT(2) +#define LATCH_EN BIT(0) + + +static ssize_t tps2483_set_value(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + val = clamp_val(val, MIN_CALIB_FACTOR, MAX_CALIB_FACTOR); + ret = regmap_write(regmap, reg, val); + return ret < 0 ? ret : count; +} + +/* Return the voltage from the given register in mV or mA */ +static int tps2483_get_value(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = regmap_read(regmap, reg, &val); + if (ret < 0) + return ret; + + switch (reg) { + case TPS2483_SHUNT_VOLT: + val = DIV_ROUND_CLOSEST(val * 25, 10000); + break; + case TPS2483_BUS_VOLT: + DIV_ROUND_CLOSEST(val * 125, 100); + break; + case TPS2483_PWR: + val = DIV_ROUND_CLOSEST(val * 125, 100); + break; + case TPS2483_CURRENT: + break; + default: + return -EINVAL; + } + return val; +} + +static ssize_t tps2483_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int value; + + value = tps2483_get_value(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +/* Voltages */ +static SENSOR_DEVICE_ATTR(in0_input, 0444, tps2483_show_value, NULL, + TPS2483_CONFIG); +static SENSOR_DEVICE_ATTR(in1_input, 0444, tps2483_show_value, NULL, + TPS2483_SHUNT_VOLT); +static SENSOR_DEVICE_ATTR(in2_input, 0444, tps2483_show_value, NULL, + TPS2483_BUS_VOLT); +static SENSOR_DEVICE_ATTR(power1_input, 0444, tps2483_show_value, NULL, + TPS2483_PWR); +static SENSOR_DEVICE_ATTR(curr1_input, 0444, tps2483_show_value, NULL, + TPS2483_CURRENT); +static SENSOR_DEVICE_ATTR(in3_input, 0644, tps2483_show_value, + tps2483_set_value, TPS2483_CALIB); + +static struct attribute *tpm2483_attrs[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(tpm2483); + +static const struct regmap_config tps2483_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = TPS2483_ALERT, +}; + +static int tps2483_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &tps2483_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, TPS2483_ALERT, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + tpm2483_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id tps2483_id[] = { + {"tps2483", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tps2483_id); + +static struct i2c_driver tps2483_driver = { + .driver = { + .name = "tps2483", + }, + .probe = tps2483_probe, + .id_table = tps2483_id, +}; + +module_i2c_driver(tps2483_driver); + +MODULE_AUTHOR("Philby John <philby.j@xxxxxxx>"); +MODULE_DESCRIPTION("TPS2483 driver"); +MODULE_LICENSE("GPL"); -- 2.13.6