diff options
Diffstat (limited to 'drivers/hwmon/pmbus')
-rw-r--r-- | drivers/hwmon/pmbus/Kconfig | 63 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/Makefile | 4 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/adm1275.c | 10 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/crps.c | 74 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/dps920ab.c | 7 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/ina233.c | 191 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/lm25066.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/lt3074.c | 122 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/ltc2978.c | 69 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/max15301.c | 1 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/max34440.c | 119 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/mpq7932.c | 4 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/mpq8785.c | 91 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/pmbus.h | 23 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/pmbus_core.c | 510 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/tda38640.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/tps25990.c | 436 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/ucd9000.c | 24 |
18 files changed, 1451 insertions, 301 deletions
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index f6d352841953..441f984a859d 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -51,7 +51,7 @@ config SENSORS_ADM1275 tristate "Analog Devices ADM1275 and compatibles" help If you say yes here you get hardware monitoring support for Analog - Devices ADM1075, ADM1272, ADM1275, ADM1276, ADM1278, ADM1281, + Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281, ADM1293, and ADM1294 Hot-Swap Controller and Digital Power Monitors. This driver can also be built as a module. If so, the module will @@ -85,6 +85,15 @@ config SENSORS_BPA_RS600 This driver can also be built as a module. If so, the module will be called bpa-rs600. +config SENSORS_CRPS + tristate "Intel Common Redundant Power Supply" + help + If you say yes here you get hardware monitoring support for the Intel + Common Redundant Power Supply. + + This driver can also be built as a module. If so, the module will + be called crps. + config SENSORS_DELTA_AHE50DC_FAN tristate "Delta AHE-50DC fan control module" help @@ -124,6 +133,15 @@ config SENSORS_DPS920AB This driver can also be built as a module. If so, the module will be called dps920ab. +config SENSORS_INA233 + tristate "Texas Instruments INA233 and compatibles" + help + If you say yes here you get hardware monitoring support for Texas + Instruments INA233. + + This driver can also be built as a module. If so, the module will + be called ina233. + config SENSORS_INSPUR_IPSPS tristate "INSPUR Power System Power Supply" help @@ -200,6 +218,24 @@ config SENSORS_LM25066_REGULATOR If you say yes here you get regulator support for National Semiconductor LM25066, LM5064, and LM5066. +config SENSORS_LT3074 + tristate "Analog Devices LT3074" + help + If you say yes here you get hardware monitoring support for Analog + Devices LT3074. + + This driver can also be built as a module. If so, the module will + be called lt3074. + +config SENSORS_LT3074_REGULATOR + tristate "Regulator support for LT3074" + depends on SENSORS_LT3074 && REGULATOR + help + If you say yes here you get regulator support for Analog Devices + LT3074. The LT3074 is a low voltage, ultralow noise, high PSRR, + dropout linear regulator. The device supplies up to 3A with a + typical dropout voltage of 45mV. + config SENSORS_LT7182S tristate "Analog Devices LT7182S" help @@ -224,9 +260,9 @@ config SENSORS_LTC2978_REGULATOR depends on SENSORS_LTC2978 && REGULATOR help If you say yes here you get regulator support for Linear Technology - LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7841, - LTC7880, LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, - LTM4686, and LTM4700. + LT7170, LT7171, LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, + LTC7841, LTC7880, LTM4644, LTM4673, LTM4675, LTM4676, LTM4677, + LTM4678, LTM4680, LTM4686, and LTM4700. config SENSORS_LTC3815 tristate "Linear Technologies LTC3815" @@ -251,7 +287,7 @@ config SENSORS_MAX15301 tristate "Maxim MAX15301" help If you say yes here you get hardware monitoring support for Maxim - MAX15301, as well as for Flex BMR461. + MAX15301, MAX15303, as well as for Flex BMR461. This driver can also be built as a module. If so, the module will be called max15301. @@ -510,6 +546,23 @@ config SENSORS_TDA38640_REGULATOR If you say yes here you get regulator support for Infineon TDA38640 as regulator. +config SENSORS_TPS25990 + tristate "TI TPS25990" + help + If you say yes here you get hardware monitoring support for TI + TPS25990. + + This driver can also be built as a module. If so, the module will + be called tps25990. + +config SENSORS_TPS25990_REGULATOR + bool "Regulator support for TPS25990 and compatibles" + depends on SENSORS_TPS25990 && REGULATOR + default SENSORS_TPS25990 + help + If you say yes here you get regulator support for Texas Instruments + TPS25990. + config SENSORS_TPS40422 tristate "TI TPS40422" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index d00bcc758b97..29cd8a3317d2 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o +obj-$(CONFIG_SENSORS_INA233) += ina233.o obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o obj-$(CONFIG_SENSORS_IR36021) += ir36021.o @@ -22,6 +23,7 @@ obj-$(CONFIG_SENSORS_IR38064) += ir38064.o obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o +obj-$(CONFIG_SENSORS_LT3074) += lt3074.o obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o @@ -51,6 +53,7 @@ obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o obj-$(CONFIG_SENSORS_TDA38640) += tda38640.o +obj-$(CONFIG_SENSORS_TPS25990) += tps25990.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o obj-$(CONFIG_SENSORS_TPS546D24) += tps546d24.o @@ -61,3 +64,4 @@ obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o obj-$(CONFIG_SENSORS_XDPE152) += xdpe152c4.o obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o +obj-$(CONFIG_SENSORS_CRPS) += crps.o diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 127593e10a03..7d175baa5de2 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -18,7 +18,7 @@ #include <linux/log2.h> #include "pmbus.h" -enum chips { adm1075, adm1272, adm1275, adm1276, adm1278, adm1281, adm1293, adm1294 }; +enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281, adm1293, adm1294 }; #define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0) #define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5) @@ -479,6 +479,7 @@ static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) static const struct i2c_device_id adm1275_id[] = { { "adm1075", adm1075 }, { "adm1272", adm1272 }, + { "adm1273", adm1273 }, { "adm1275", adm1275 }, { "adm1276", adm1276 }, { "adm1278", adm1278 }, @@ -555,9 +556,9 @@ static int adm1275_probe(struct i2c_client *client) "Device mismatch: Configured %s, detected %s\n", client->name, mid->name); - if (mid->driver_data == adm1272 || mid->driver_data == adm1278 || - mid->driver_data == adm1281 || mid->driver_data == adm1293 || - mid->driver_data == adm1294) + if (mid->driver_data == adm1272 || mid->driver_data == adm1273 || + mid->driver_data == adm1278 || mid->driver_data == adm1281 || + mid->driver_data == adm1293 || mid->driver_data == adm1294) config_read_fn = i2c_smbus_read_word_data; else config_read_fn = i2c_smbus_read_byte_data; @@ -630,6 +631,7 @@ static int adm1275_probe(struct i2c_client *client) PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; break; case adm1272: + case adm1273: data->have_vout = true; data->have_pin_max = true; data->have_temp_max = true; diff --git a/drivers/hwmon/pmbus/crps.c b/drivers/hwmon/pmbus/crps.c new file mode 100644 index 000000000000..164b33fed312 --- /dev/null +++ b/drivers/hwmon/pmbus/crps.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2024 IBM Corp. + */ + +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/pmbus.h> + +#include "pmbus.h" + +static const struct i2c_device_id crps_id[] = { + { "intel_crps185" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, crps_id); + +static struct pmbus_driver_info crps_info = { + .pages = 1, + /* PSU uses default linear data format. */ + .func[0] = PMBUS_HAVE_PIN | PMBUS_HAVE_IOUT | + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_IIN | + PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12, +}; + +static int crps_probe(struct i2c_client *client) +{ + int rc; + struct device *dev = &client->dev; + char buf[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + + rc = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (rc < 0) + return dev_err_probe(dev, rc, "Failed to read PMBUS_MFR_MODEL\n"); + + if (rc != 7 || strncmp(buf, "03NK260", 7)) { + buf[rc] = '\0'; + return dev_err_probe(dev, -ENODEV, "Model '%s' not supported\n", buf); + } + + rc = pmbus_do_probe(client, &crps_info); + if (rc) + return dev_err_probe(dev, rc, "Failed to probe\n"); + + return 0; +} + +static const struct of_device_id crps_of_match[] = { + { + .compatible = "intel,crps185", + }, + {} +}; +MODULE_DEVICE_TABLE(of, crps_of_match); + +static struct i2c_driver crps_driver = { + .driver = { + .name = "crps", + .of_match_table = crps_of_match, + }, + .probe = crps_probe, + .id_table = crps_id, +}; + +module_i2c_driver(crps_driver); + +MODULE_AUTHOR("Ninad Palsule"); +MODULE_DESCRIPTION("PMBus driver for Intel Common Redundant power supplies"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c index cc5aac9dfdb3..325111a955e6 100644 --- a/drivers/hwmon/pmbus/dps920ab.c +++ b/drivers/hwmon/pmbus/dps920ab.c @@ -190,12 +190,19 @@ static const struct of_device_id __maybe_unused dps920ab_of_match[] = { MODULE_DEVICE_TABLE(of, dps920ab_of_match); +static const struct i2c_device_id dps920ab_device_id[] = { + { "dps920ab" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, dps920ab_device_id); + static struct i2c_driver dps920ab_driver = { .driver = { .name = "dps920ab", .of_match_table = of_match_ptr(dps920ab_of_match), }, .probe = dps920ab_probe, + .id_table = dps920ab_device_id, }; module_i2c_driver(dps920ab_driver); diff --git a/drivers/hwmon/pmbus/ina233.c b/drivers/hwmon/pmbus/ina233.c new file mode 100644 index 000000000000..dde1e1678394 --- /dev/null +++ b/drivers/hwmon/pmbus/ina233.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for ina233 + * + * Copyright (c) 2025 Leo Yang + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +#define MFR_READ_VSHUNT 0xd1 +#define MFR_CALIBRATION 0xd4 + +#define INA233_MAX_CURRENT_DEFAULT 32768000 /* uA */ +#define INA233_RSHUNT_DEFAULT 2000 /* uOhm */ + +#define MAX_M_VAL 32767 + +static void calculate_coef(int *m, int *R, u32 current_lsb, int power_coef) +{ + u64 scaled_m; + int scale_factor = 0; + int scale_coef = 1; + + /* + * 1000000 from Current_LSB A->uA . + * scale_coef is for scaling up to minimize rounding errors, + * If there is no decimal information, no need to scale. + */ + if (1000000 % current_lsb) { + /* Scaling to keep integer precision */ + scale_factor = -3; + scale_coef = 1000; + } + + /* + * Unit Conversion (Current_LSB A->uA) and use scaling(scale_factor) + * to keep integer precision. + * Formulae referenced from spec. + */ + scaled_m = div64_u64(1000000 * scale_coef, (u64)current_lsb * power_coef); + + /* Maximize while keeping it bounded.*/ + while (scaled_m > MAX_M_VAL) { + scaled_m = div_u64(scaled_m, 10); + scale_factor++; + } + /* Scale up only if fractional part exists. */ + while (scaled_m * 10 < MAX_M_VAL && scale_coef != 1) { + scaled_m *= 10; + scale_factor--; + } + + *m = scaled_m; + *R = scale_factor; +} + +static int ina233_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, 0, 0xff, MFR_READ_VSHUNT); + + /* Adjust returned value to match VIN coefficients */ + /* VIN: 1.25 mV VSHUNT: 2.5 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 25, 12500); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int ina233_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + int ret, m, R; + u32 rshunt; + u32 max_current; + u32 current_lsb; + u16 calibration; + struct pmbus_driver_info *info; + + info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pages = 1; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_VOLTAGE_OUT] = direct; + info->format[PSC_CURRENT_OUT] = direct; + info->format[PSC_POWER] = direct; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT + | PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON; + info->m[PSC_VOLTAGE_IN] = 8; + info->R[PSC_VOLTAGE_IN] = 2; + info->m[PSC_VOLTAGE_OUT] = 8; + info->R[PSC_VOLTAGE_OUT] = 2; + info->read_word_data = ina233_read_word_data; + + /* If INA233 skips current/power, shunt-resistor and current-lsb aren't needed. */ + /* read rshunt value (uOhm) */ + ret = device_property_read_u32(dev, "shunt-resistor", &rshunt); + if (ret) { + if (ret != -EINVAL) + return dev_err_probe(dev, ret, "Shunt resistor property read fail.\n"); + rshunt = INA233_RSHUNT_DEFAULT; + } + if (!rshunt) + return dev_err_probe(dev, -EINVAL, + "Shunt resistor cannot be zero.\n"); + + /* read Maximum expected current value (uA) */ + ret = device_property_read_u32(dev, "ti,maximum-expected-current-microamp", &max_current); + if (ret) { + if (ret != -EINVAL) + return dev_err_probe(dev, ret, + "Maximum expected current property read fail.\n"); + max_current = INA233_MAX_CURRENT_DEFAULT; + } + if (max_current < 32768) + return dev_err_probe(dev, -EINVAL, + "Maximum expected current cannot less then 32768.\n"); + + /* Calculate Current_LSB according to the spec formula */ + current_lsb = max_current / 32768; + + /* calculate current coefficient */ + calculate_coef(&m, &R, current_lsb, 1); + info->m[PSC_CURRENT_OUT] = m; + info->R[PSC_CURRENT_OUT] = R; + + /* calculate power coefficient */ + calculate_coef(&m, &R, current_lsb, 25); + info->m[PSC_POWER] = m; + info->R[PSC_POWER] = R; + + /* write MFR_CALIBRATION register, Apply formula from spec with unit scaling. */ + calibration = div64_u64(5120000000ULL, (u64)rshunt * current_lsb); + if (calibration > 0x7FFF) + return dev_err_probe(dev, -EINVAL, + "Product of Current_LSB %u and shunt resistor %u too small, MFR_CALIBRATION reg exceeds 0x7FFF.\n", + current_lsb, rshunt); + ret = i2c_smbus_write_word_data(client, MFR_CALIBRATION, calibration); + if (ret < 0) + return dev_err_probe(dev, ret, "Unable to write calibration.\n"); + + dev_dbg(dev, "power monitor %s (Rshunt = %u uOhm, Current_LSB = %u uA/bit)\n", + client->name, rshunt, current_lsb); + + return pmbus_do_probe(client, info); +} + +static const struct i2c_device_id ina233_id[] = { + {"ina233", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ina233_id); + +static const struct of_device_id __maybe_unused ina233_of_match[] = { + { .compatible = "ti,ina233" }, + {} +}; +MODULE_DEVICE_TABLE(of, ina233_of_match); + +static struct i2c_driver ina233_driver = { + .driver = { + .name = "ina233", + .of_match_table = of_match_ptr(ina233_of_match), + }, + .probe = ina233_probe, + .id_table = ina233_id, +}; + +module_i2c_driver(ina233_driver); + +MODULE_AUTHOR("Leo Yang <leo.yang.sy0@gmail.com>"); +MODULE_DESCRIPTION("PMBus driver for INA233 and compatible chips"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index 40b0dda32ea6..dd7275a67a0a 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -437,7 +437,7 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, #if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR) static const struct regulator_desc lm25066_reg_desc[] = { - PMBUS_REGULATOR_ONE("vout"), + PMBUS_REGULATOR_ONE_NODE("vout"), }; #endif diff --git a/drivers/hwmon/pmbus/lt3074.c b/drivers/hwmon/pmbus/lt3074.c new file mode 100644 index 000000000000..3704dbe7b54a --- /dev/null +++ b/drivers/hwmon/pmbus/lt3074.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hardware monitoring driver for Analog Devices LT3074 + * + * Copyright (C) 2025 Analog Devices, Inc. + */ +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> + +#include "pmbus.h" + +#define LT3074_MFR_READ_VBIAS 0xc6 +#define LT3074_MFR_BIAS_OV_WARN_LIMIT 0xc7 +#define LT3074_MFR_BIAS_UV_WARN_LIMIT 0xc8 +#define LT3074_MFR_SPECIAL_ID 0xe7 + +#define LT3074_SPECIAL_ID_VALUE 0x1c1d + +static const struct regulator_desc __maybe_unused lt3074_reg_desc[] = { + PMBUS_REGULATOR_ONE("regulator"), +}; + +static int lt3074_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + switch (reg) { + case PMBUS_VIRT_READ_VMON: + return pmbus_read_word_data(client, page, phase, + LT3074_MFR_READ_VBIAS); + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + return pmbus_read_word_data(client, page, phase, + LT3074_MFR_BIAS_UV_WARN_LIMIT); + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + return pmbus_read_word_data(client, page, phase, + LT3074_MFR_BIAS_OV_WARN_LIMIT); + default: + return -ENODATA; + } +} + +static int lt3074_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + switch (reg) { + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + return pmbus_write_word_data(client, 0, + LT3074_MFR_BIAS_UV_WARN_LIMIT, + word); + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + return pmbus_write_word_data(client, 0, + LT3074_MFR_BIAS_OV_WARN_LIMIT, + word); + default: + return -ENODATA; + } +} + +static struct pmbus_driver_info lt3074_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_VMON | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, + .read_word_data = lt3074_read_word_data, + .write_word_data = lt3074_write_word_data, +#if IS_ENABLED(CONFIG_SENSORS_LT3074_REGULATOR) + .num_regulators = 1, + .reg_desc = lt3074_reg_desc, +#endif +}; + +static int lt3074_probe(struct i2c_client *client) +{ + int ret; + struct device *dev = &client->dev; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_word_data(client, LT3074_MFR_SPECIAL_ID); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read ID\n"); + + if (ret != LT3074_SPECIAL_ID_VALUE) + return dev_err_probe(dev, -ENODEV, "ID mismatch\n"); + + return pmbus_do_probe(client, <3074_info); +} + +static const struct i2c_device_id lt3074_id[] = { + { "lt3074", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, lt3074_id); + +static const struct of_device_id __maybe_unused lt3074_of_match[] = { + { .compatible = "adi,lt3074" }, + {} +}; +MODULE_DEVICE_TABLE(of, lt3074_of_match); + +static struct i2c_driver lt3074_driver = { + .driver = { + .name = "lt3074", + .of_match_table = of_match_ptr(lt3074_of_match), + }, + .probe = lt3074_probe, + .id_table = lt3074_id, +}; +module_i2c_driver(lt3074_driver); + +MODULE_AUTHOR("Cedric Encarnacion <cedricjustine.encarnacion@analog.com>"); +MODULE_DESCRIPTION("PMBus driver for Analog Devices LT3074"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 4c306943383a..8f5be520a15d 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -23,11 +23,11 @@ enum chips { /* Managers */ ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980, /* Controllers */ - ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, - ltc7841, ltc7880, + lt7170, lt7171, ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, + ltc3889, ltc7132, ltc7841, ltc7880, /* Modules */ - ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686, - ltm4700, + ltm2987, ltm4664, ltm4673, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, + ltm4686, ltm4700, }; /* Common for all chips */ @@ -62,6 +62,7 @@ enum chips { #define LTC2978_ID_MASK 0xfff0 +#define LT7170_ID 0x1C10 #define LTC2972_ID 0x0310 #define LTC2974_ID 0x0210 #define LTC2975_ID 0x0220 @@ -86,6 +87,8 @@ enum chips { #define LTM2987_ID_A 0x8010 /* A/B for two die IDs */ #define LTM2987_ID_B 0x8020 #define LTM4664_ID 0x4120 +#define LTM4673_ID_REV1 0x0230 +#define LTM4673_ID 0x4480 #define LTM4675_ID 0x47a0 #define LTM4676_ID_REV1 0x4400 #define LTM4676_ID_REV2 0x4480 @@ -535,6 +538,8 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc2978_id[] = { + {"lt7170", lt7170}, + {"lt7171", lt7171}, {"ltc2972", ltc2972}, {"ltc2974", ltc2974}, {"ltc2975", ltc2975}, @@ -554,6 +559,7 @@ static const struct i2c_device_id ltc2978_id[] = { {"ltc7880", ltc7880}, {"ltm2987", ltm2987}, {"ltm4664", ltm4664}, + {"ltm4673", ltm4673}, {"ltm4675", ltm4675}, {"ltm4676", ltm4676}, {"ltm4677", ltm4677}, @@ -612,7 +618,7 @@ static int ltc2978_get_id(struct i2c_client *client) ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); if (ret < 0) return ret; - if (ret < 3 || strncmp(buf, "LTC", 3)) + if (ret < 3 || (strncmp(buf, "LTC", 3) && strncmp(buf, "ADI", 3))) return -ENODEV; ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); @@ -627,6 +633,25 @@ static int ltc2978_get_id(struct i2c_client *client) chip_id &= LTC2978_ID_MASK; + if (chip_id == LT7170_ID) { + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID, + sizeof(buf), buf); + if (ret < 0) + return ret; + + if (!strncmp(buf + 1, "LT7170", 6) || + !strncmp(buf + 1, "LT7170-1", 8)) + return lt7170; + if (!strncmp(buf + 1, "LT7171", 6) || + !strncmp(buf + 1, "LT7171-1", 8)) + return lt7171; + + return -ENODEV; + } + if (chip_id == LTC2972_ID) return ltc2972; else if (chip_id == LTC2974_ID) @@ -665,6 +690,8 @@ static int ltc2978_get_id(struct i2c_client *client) return ltm2987; else if (chip_id == LTM4664_ID) return ltm4664; + else if (chip_id == LTM4673_ID || chip_id == LTM4673_ID_REV1) + return ltm4673; else if (chip_id == LTM4675_ID) return ltm4675; else if (chip_id == LTM4676_ID_REV1 || chip_id == LTM4676_ID_REV2 || @@ -736,6 +763,20 @@ static int ltc2978_probe(struct i2c_client *client) data->temp2_max = 0x7c00; switch (data->id) { + case lt7170: + case lt7171: + data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; + info->read_word_data = ltc3883_read_word_data; + info->pages = LTC3883_NUM_PAGES; + info->format[PSC_VOLTAGE_IN] = ieee754; + info->format[PSC_VOLTAGE_OUT] = ieee754; + info->format[PSC_CURRENT_OUT] = ieee754; + info->format[PSC_TEMPERATURE] = ieee754; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + break; case ltc2972: info->read_word_data = ltc2975_read_word_data; info->pages = LTC2972_NUM_PAGES; @@ -869,6 +910,21 @@ static int ltc2978_probe(struct i2c_client *client) | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; break; + case ltm4673: + data->features |= FEAT_NEEDS_POLLING; + info->read_word_data = ltc2975_read_word_data; + info->pages = LTC2974_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP2; + for (i = 0; i < info->pages; i++) { + info->func[i] |= PMBUS_HAVE_IIN + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_PIN + | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + } + break; default: return -ENODEV; } @@ -907,6 +963,8 @@ static int ltc2978_probe(struct i2c_client *client) #ifdef CONFIG_OF static const struct of_device_id ltc2978_of_match[] = { + { .compatible = "lltc,lt7170" }, + { .compatible = "lltc,lt7171" }, { .compatible = "lltc,ltc2972" }, { .compatible = "lltc,ltc2974" }, { .compatible = "lltc,ltc2975" }, @@ -926,6 +984,7 @@ static const struct of_device_id ltc2978_of_match[] = { { .compatible = "lltc,ltc7880" }, { .compatible = "lltc,ltm2987" }, { .compatible = "lltc,ltm4664" }, + { .compatible = "lltc,ltm4673" }, { .compatible = "lltc,ltm4675" }, { .compatible = "lltc,ltm4676" }, { .compatible = "lltc,ltm4677" }, diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c index 50dfd477772f..d5810b88ea8d 100644 --- a/drivers/hwmon/pmbus/max15301.c +++ b/drivers/hwmon/pmbus/max15301.c @@ -25,6 +25,7 @@ static const struct i2c_device_id max15301_id[] = { { "bmr461" }, { "max15301" }, + { "max15303" }, {} }; MODULE_DEVICE_TABLE(i2c, max15301_id); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index c9dda33831ff..56834d26f8ef 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -12,9 +12,26 @@ #include <linux/init.h> #include <linux/err.h> #include <linux/i2c.h> +#include <linux/delay.h> #include "pmbus.h" -enum chips { max34440, max34441, max34446, max34451, max34460, max34461 }; +enum chips { + adpm12160, + max34440, + max34441, + max34446, + max34451, + max34460, + max34461, +}; + +/* + * Firmware is sometimes not ready if we try and read the + * data from the page immediately after setting. Maxim + * recommends 50us delay due to the chip failing to clock + * stretch long enough here. + */ +#define MAX34440_PAGE_CHANGE_DELAY 50 #define MAX34440_MFR_VOUT_PEAK 0xd4 #define MAX34440_MFR_IOUT_PEAK 0xd5 @@ -34,16 +51,21 @@ enum chips { max34440, max34441, max34446, max34451, max34460, max34461 }; /* * The whole max344* family have IOUT_OC_WARN_LIMIT and IOUT_OC_FAULT_LIMIT * swapped from the standard pmbus spec addresses. + * For max34451, version MAX34451ETNA6+ and later has this issue fixed. */ #define MAX34440_IOUT_OC_WARN_LIMIT 0x46 #define MAX34440_IOUT_OC_FAULT_LIMIT 0x4A +#define MAX34451ETNA6_MFR_REV 0x0012 + #define MAX34451_MFR_CHANNEL_CONFIG 0xe4 #define MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK 0x3f struct max34440_data { int id; struct pmbus_driver_info info; + u8 iout_oc_warn_limit; + u8 iout_oc_fault_limit; }; #define to_max34440_data(x) container_of(x, struct max34440_data, info) @@ -60,11 +82,11 @@ static int max34440_read_word_data(struct i2c_client *client, int page, switch (reg) { case PMBUS_IOUT_OC_FAULT_LIMIT: ret = pmbus_read_word_data(client, page, phase, - MAX34440_IOUT_OC_FAULT_LIMIT); + data->iout_oc_fault_limit); break; case PMBUS_IOUT_OC_WARN_LIMIT: ret = pmbus_read_word_data(client, page, phase, - MAX34440_IOUT_OC_WARN_LIMIT); + data->iout_oc_warn_limit); break; case PMBUS_VIRT_READ_VOUT_MIN: ret = pmbus_read_word_data(client, page, phase, @@ -75,7 +97,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page, MAX34440_MFR_VOUT_PEAK); break; case PMBUS_VIRT_READ_IOUT_AVG: - if (data->id != max34446 && data->id != max34451) + if (data->id != max34446 && data->id != max34451 && + data->id != adpm12160) return -ENXIO; ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_IOUT_AVG); @@ -133,11 +156,11 @@ static int max34440_write_word_data(struct i2c_client *client, int page, switch (reg) { case PMBUS_IOUT_OC_FAULT_LIMIT: - ret = pmbus_write_word_data(client, page, MAX34440_IOUT_OC_FAULT_LIMIT, + ret = pmbus_write_word_data(client, page, data->iout_oc_fault_limit, word); break; case PMBUS_IOUT_OC_WARN_LIMIT: - ret = pmbus_write_word_data(client, page, MAX34440_IOUT_OC_WARN_LIMIT, + ret = pmbus_write_word_data(client, page, data->iout_oc_warn_limit, word); break; case PMBUS_VIRT_RESET_POUT_HISTORY: @@ -159,7 +182,8 @@ static int max34440_write_word_data(struct i2c_client *client, int page, case PMBUS_VIRT_RESET_IOUT_HISTORY: ret = pmbus_write_word_data(client, page, MAX34440_MFR_IOUT_PEAK, 0); - if (!ret && (data->id == max34446 || data->id == max34451)) + if (!ret && (data->id == max34446 || data->id == max34451 || + data->id == adpm12160)) ret = pmbus_write_word_data(client, page, MAX34446_MFR_IOUT_AVG, 0); @@ -235,9 +259,29 @@ static int max34451_set_supported_funcs(struct i2c_client *client, */ int page, rv; + bool max34451_na6 = false; + + rv = i2c_smbus_read_word_data(client, PMBUS_MFR_REVISION); + if (rv < 0) + return rv; + + if (rv >= MAX34451ETNA6_MFR_REV) { + max34451_na6 = true; + data->info.format[PSC_VOLTAGE_IN] = direct; + data->info.format[PSC_CURRENT_IN] = direct; + data->info.m[PSC_VOLTAGE_IN] = 1; + data->info.b[PSC_VOLTAGE_IN] = 0; + data->info.R[PSC_VOLTAGE_IN] = 3; + data->info.m[PSC_CURRENT_IN] = 1; + data->info.b[PSC_CURRENT_IN] = 0; + data->info.R[PSC_CURRENT_IN] = 2; + data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT; + data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT; + } for (page = 0; page < 16; page++) { rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + fsleep(MAX34440_PAGE_CHANGE_DELAY); if (rv < 0) return rv; @@ -251,16 +295,30 @@ static int max34451_set_supported_funcs(struct i2c_client *client, case 0x20: data->info.func[page] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + + if (max34451_na6) + data->info.func[page] |= PMBUS_HAVE_VIN | + PMBUS_HAVE_STATUS_INPUT; break; case 0x21: data->info.func[page] = PMBUS_HAVE_VOUT; + + if (max34451_na6) + data->info.func[page] |= PMBUS_HAVE_VIN; break; case 0x22: data->info.func[page] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + + if (max34451_na6) + data->info.func[page] |= PMBUS_HAVE_IIN | + PMBUS_HAVE_STATUS_INPUT; break; case 0x23: data->info.func[page] = PMBUS_HAVE_IOUT; + + if (max34451_na6) + data->info.func[page] |= PMBUS_HAVE_IIN; break; default: break; @@ -271,6 +329,41 @@ static int max34451_set_supported_funcs(struct i2c_client *client, } static struct pmbus_driver_info max34440_info[] = { + [adpm12160] = { + .pages = 19, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 1, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 0, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 0, + .m[PSC_CURRENT_IN] = 1, + .b[PSC_CURRENT_IN] = 0, + .R[PSC_CURRENT_IN] = 2, + .m[PSC_CURRENT_OUT] = 1, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 2, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + /* absent func below [18] are not for monitoring */ + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[5] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[6] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[7] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[8] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT, + .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, + .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, [max34440] = { .pages = 14, .format[PSC_VOLTAGE_IN] = direct, @@ -312,6 +405,7 @@ static struct pmbus_driver_info max34440_info[] = { .read_byte_data = max34440_read_byte_data, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, [max34441] = { .pages = 12, @@ -355,6 +449,7 @@ static struct pmbus_driver_info max34440_info[] = { .read_byte_data = max34440_read_byte_data, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, [max34446] = { .pages = 7, @@ -392,6 +487,7 @@ static struct pmbus_driver_info max34440_info[] = { .read_byte_data = max34440_read_byte_data, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, [max34451] = { .pages = 21, @@ -415,6 +511,7 @@ static struct pmbus_driver_info max34440_info[] = { .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, [max34460] = { .pages = 18, @@ -445,6 +542,7 @@ static struct pmbus_driver_info max34440_info[] = { .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, [max34461] = { .pages = 23, @@ -480,6 +578,7 @@ static struct pmbus_driver_info max34440_info[] = { .func[21] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, + .page_change_delay = MAX34440_PAGE_CHANGE_DELAY, }, }; @@ -494,17 +593,23 @@ static int max34440_probe(struct i2c_client *client) return -ENOMEM; data->id = i2c_match_id(max34440_id, client)->driver_data; data->info = max34440_info[data->id]; + data->iout_oc_fault_limit = MAX34440_IOUT_OC_FAULT_LIMIT; + data->iout_oc_warn_limit = MAX34440_IOUT_OC_WARN_LIMIT; if (data->id == max34451) { rv = max34451_set_supported_funcs(client, data); if (rv) return rv; + } else if (data->id == adpm12160) { + data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT; + data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT; } return pmbus_do_probe(client, &data->info); } static const struct i2c_device_id max34440_id[] = { + {"adpm12160", adpm12160}, {"max34440", max34440}, {"max34441", max34441}, {"max34446", max34446}, diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c index c1e2d0cb2fd0..8f10e37a7a76 100644 --- a/drivers/hwmon/pmbus/mpq7932.c +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -51,8 +51,8 @@ static const struct regulator_desc mpq7932_regulators_desc[] = { }; static const struct regulator_desc mpq7932_regulators_desc_one[] = { - PMBUS_REGULATOR_STEP_ONE("buck", MPQ7932_N_VOLTAGES, - MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), + PMBUS_REGULATOR_STEP_ONE_NODE("buck", MPQ7932_N_VOLTAGES, + MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), }; #endif diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c index 331c274ca892..1f56aaf4dde8 100644 --- a/drivers/hwmon/pmbus/mpq8785.c +++ b/drivers/hwmon/pmbus/mpq8785.c @@ -4,10 +4,23 @@ */ #include <linux/i2c.h> +#include <linux/bitops.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/of_device.h> #include "pmbus.h" +#define MPM82504_READ_TEMPERATURE_1_SIGN_POS 9 + +enum chips { mpm3695, mpm3695_25, mpm82504, mpq8785 }; + +static u16 voltage_scale_loop_max_val[] = { + [mpm3695] = GENMASK(9, 0), + [mpm3695_25] = GENMASK(11, 0), + [mpm82504] = GENMASK(9, 0), + [mpq8785] = GENMASK(10, 0), +}; + static int mpq8785_identify(struct i2c_client *client, struct pmbus_driver_info *info) { @@ -34,6 +47,20 @@ static int mpq8785_identify(struct i2c_client *client, return 0; }; +static int mpm82504_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + ret = pmbus_read_word_data(client, page, phase, reg); + + if (ret < 0 || reg != PMBUS_READ_TEMPERATURE_1) + return ret; + + /* Fix PMBUS_READ_TEMPERATURE_1 signedness */ + return sign_extend32(ret, MPM82504_READ_TEMPERATURE_1_SIGN_POS) & 0xffff; +} + static struct pmbus_driver_info mpq8785_info = { .pages = 1, .format[PSC_VOLTAGE_IN] = direct, @@ -53,26 +80,74 @@ static struct pmbus_driver_info mpq8785_info = { PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, - .identify = mpq8785_identify, -}; - -static int mpq8785_probe(struct i2c_client *client) -{ - return pmbus_do_probe(client, &mpq8785_info); }; static const struct i2c_device_id mpq8785_id[] = { - { "mpq8785" }, + { "mpm3695", mpm3695 }, + { "mpm3695-25", mpm3695_25 }, + { "mpm82504", mpm82504 }, + { "mpq8785", mpq8785 }, { }, }; MODULE_DEVICE_TABLE(i2c, mpq8785_id); static const struct of_device_id __maybe_unused mpq8785_of_match[] = { - { .compatible = "mps,mpq8785" }, + { .compatible = "mps,mpm3695", .data = (void *)mpm3695 }, + { .compatible = "mps,mpm3695-25", .data = (void *)mpm3695_25 }, + { .compatible = "mps,mpm82504", .data = (void *)mpm82504 }, + { .compatible = "mps,mpq8785", .data = (void *)mpq8785 }, {} }; MODULE_DEVICE_TABLE(of, mpq8785_of_match); +static int mpq8785_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pmbus_driver_info *info; + enum chips chip_id; + u32 voltage_scale; + int ret; + + info = devm_kmemdup(dev, &mpq8785_info, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + if (dev->of_node) + chip_id = (kernel_ulong_t)of_device_get_match_data(dev); + else + chip_id = (kernel_ulong_t)i2c_get_match_data(client); + + switch (chip_id) { + case mpm3695: + case mpm3695_25: + case mpm82504: + info->format[PSC_VOLTAGE_OUT] = direct; + info->m[PSC_VOLTAGE_OUT] = 8; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = 2; + info->read_word_data = mpm82504_read_word_data; + break; + case mpq8785: + info->identify = mpq8785_identify; + break; + default: + return -ENODEV; + } + + if (!device_property_read_u32(dev, "mps,vout-fb-divider-ratio-permille", + &voltage_scale)) { + if (voltage_scale > voltage_scale_loop_max_val[chip_id]) + return -EINVAL; + + ret = i2c_smbus_write_word_data(client, PMBUS_VOUT_SCALE_LOOP, + voltage_scale); + if (ret) + return ret; + } + + return pmbus_do_probe(client, info); +}; + static struct i2c_driver mpq8785_driver = { .driver = { .name = "mpq8785", diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index d605412a3173..d2e9bfb5320f 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -482,11 +482,14 @@ struct pmbus_driver_info { */ int access_delay; /* in microseconds */ int write_delay; /* in microseconds */ + int page_change_delay; /* in microseconds */ }; /* Regulator ops */ extern const struct regulator_ops pmbus_regulator_ops; +int pmbus_regulator_init_cb(struct regulator_dev *rdev, + struct regulator_config *config); /* Macros for filling in array of struct regulator_desc */ #define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step, _min_uV) \ @@ -501,24 +504,38 @@ extern const struct regulator_ops pmbus_regulator_ops; .n_voltages = _voltages, \ .uV_step = _step, \ .min_uV = _min_uV, \ + .init_cb = pmbus_regulator_init_cb, \ } #define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0, 0) -#define PMBUS_REGULATOR_STEP_ONE(_name, _voltages, _step, _min_uV) \ +#define __PMBUS_REGULATOR_STEP_ONE(_name, _node, _voltages, _step, _min_uV) \ { \ .name = (_name), \ .of_match = of_match_ptr(_name), \ - .regulators_node = of_match_ptr("regulators"), \ + .regulators_node = of_match_ptr(_node), \ .ops = &pmbus_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ .n_voltages = _voltages, \ .uV_step = _step, \ .min_uV = _min_uV, \ + .init_cb = pmbus_regulator_init_cb, \ } -#define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0) +/* + * _NODE macros are defined for historic reasons and MUST NOT be used in new + * drivers. + */ +#define PMBUS_REGULATOR_STEP_ONE_NODE(_name, _voltages, _step, _min_uV) \ + __PMBUS_REGULATOR_STEP_ONE(_name, "regulators", _voltages, _step, _min_uV) + +#define PMBUS_REGULATOR_ONE_NODE(_name) PMBUS_REGULATOR_STEP_ONE_NODE(_name, 0, 0, 0) + +#define PMBUS_REGULATOR_STEP_ONE(_name, _voltages, _step, _min_uV) \ + __PMBUS_REGULATOR_STEP_ONE(_name, NULL, _voltages, _step, _min_uV) + +#define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0) /* Function declarations */ diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index a1375cb6b648..be6d05def115 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -8,6 +8,7 @@ #include <linux/debugfs.h> #include <linux/delay.h> +#include <linux/dcache.h> #include <linux/kernel.h> #include <linux/math64.h> #include <linux/module.h> @@ -31,6 +32,16 @@ #define PMBUS_ATTR_ALLOC_SIZE 32 #define PMBUS_NAME_SIZE 24 +/* + * The type of operation used for picking the delay between + * successive pmbus operations. + */ +#define PMBUS_OP_WRITE BIT(0) +#define PMBUS_OP_PAGE_CHANGE BIT(1) + +static int wp = -1; +module_param(wp, int, 0444); + struct pmbus_sensor { struct pmbus_sensor *next; char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ @@ -41,8 +52,7 @@ struct pmbus_sensor { enum pmbus_sensor_classes class; /* sensor class */ bool update; /* runtime sensor update needed */ bool convert; /* Whether or not to apply linear/vid/direct */ - int data; /* Sensor data. - Negative if there was a read error */ + int data; /* Sensor data; negative if there was a read error */ }; #define to_pmbus_sensor(_attr) \ container_of(_attr, struct pmbus_sensor, attribute) @@ -97,7 +107,6 @@ struct pmbus_data { int num_attributes; struct attribute_group group; const struct attribute_group **groups; - struct dentry *debugfs; /* debugfs device directory */ struct pmbus_sensor *sensors; @@ -111,8 +120,8 @@ struct pmbus_data { int vout_low[PMBUS_PAGES]; /* voltage low margin */ int vout_high[PMBUS_PAGES]; /* voltage high margin */ - ktime_t write_time; /* Last SMBUS write timestamp */ - ktime_t access_time; /* Last SMBUS access timestamp */ + + ktime_t next_access_backoff; /* Wait until at least this time */ }; struct pmbus_debugfs_entry { @@ -167,33 +176,26 @@ EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS"); static void pmbus_wait(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); - const struct pmbus_driver_info *info = data->info; - s64 delta; - - if (info->access_delay) { - delta = ktime_us_delta(ktime_get(), data->access_time); - - if (delta < info->access_delay) - fsleep(info->access_delay - delta); - } else if (info->write_delay) { - delta = ktime_us_delta(ktime_get(), data->write_time); + s64 delay = ktime_us_delta(data->next_access_backoff, ktime_get()); - if (delta < info->write_delay) - fsleep(info->write_delay - delta); - } + if (delay > 0) + fsleep(delay); } -/* Sets the last accessed timestamp for pmbus_wait */ -static void pmbus_update_ts(struct i2c_client *client, bool write_op) +/* Sets the last operation timestamp for pmbus_wait */ +static void pmbus_update_ts(struct i2c_client *client, int op) { struct pmbus_data *data = i2c_get_clientdata(client); const struct pmbus_driver_info *info = data->info; + int delay = info->access_delay; - if (info->access_delay) { - data->access_time = ktime_get(); - } else if (info->write_delay && write_op) { - data->write_time = ktime_get(); - } + if (op & PMBUS_OP_WRITE) + delay = max(delay, info->write_delay); + if (op & PMBUS_OP_PAGE_CHANGE) + delay = max(delay, info->page_change_delay); + + if (delay > 0) + data->next_access_backoff = ktime_add_us(ktime_get(), delay); } int pmbus_set_page(struct i2c_client *client, int page, int phase) @@ -208,13 +210,13 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) data->info->pages > 1 && page != data->currpage) { pmbus_wait(client); rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE | PMBUS_OP_PAGE_CHANGE); if (rv < 0) return rv; pmbus_wait(client); rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); if (rv < 0) return rv; @@ -228,7 +230,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) pmbus_wait(client); rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, phase); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE); if (rv) return rv; } @@ -248,7 +250,7 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value) pmbus_wait(client); rv = i2c_smbus_write_byte(client, value); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE); return rv; } @@ -283,13 +285,12 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, pmbus_wait(client); rv = i2c_smbus_write_word_data(client, reg, word); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE); return rv; } EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, "PMBUS"); - static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg, u16 word) { @@ -378,14 +379,14 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id, u8 to; from = _pmbus_read_byte_data(client, page, - pmbus_fan_config_registers[id]); + pmbus_fan_config_registers[id]); if (from < 0) return from; to = (from & ~mask) | (config & mask); if (to != from) { rv = _pmbus_write_byte_data(client, page, - pmbus_fan_config_registers[id], to); + pmbus_fan_config_registers[id], to); if (rv < 0) return rv; } @@ -405,7 +406,7 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) pmbus_wait(client); rv = i2c_smbus_read_word_data(client, reg); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); return rv; } @@ -468,7 +469,7 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) pmbus_wait(client); rv = i2c_smbus_read_byte_data(client, reg); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); return rv; } @@ -484,7 +485,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) pmbus_wait(client); rv = i2c_smbus_write_byte_data(client, reg, value); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE); return rv; } @@ -520,7 +521,7 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, pmbus_wait(client); rv = i2c_smbus_read_block_data(client, reg, data_buf); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); return rv; } @@ -560,7 +561,7 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id, } config = _pmbus_read_byte_data(client, page, - pmbus_fan_config_registers[id]); + pmbus_fan_config_registers[id]); if (config < 0) return config; @@ -785,7 +786,7 @@ static s64 pmbus_reg2data_linear(struct pmbus_data *data, if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ exponent = data->exponent[sensor->page]; - mantissa = (u16) sensor->data; + mantissa = (u16)sensor->data; } else { /* LINEAR11 */ exponent = ((s16)sensor->data) >> 11; mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5; @@ -1170,7 +1171,6 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b, } else { pmbus_clear_fault_page(client, page); } - } if (s1 && s2) { s64 v1, v2; @@ -1467,8 +1467,7 @@ static int pmbus_add_label(struct pmbus_data *data, snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); if (!index) { if (phase == 0xff) - strncpy(label->label, lstring, - sizeof(label->label) - 1); + strscpy(label->label, lstring); else snprintf(label->label, sizeof(label->label), "%s.%d", lstring, phase); @@ -1497,8 +1496,7 @@ struct pmbus_limit_attr { u16 reg; /* Limit register */ u16 sbit; /* Alarm attribute status bit */ bool update; /* True if register needs updates */ - bool low; /* True if low limit; for limits with compare - functions only */ + bool low; /* True if low limit; for limits with compare functions only */ const char *attr; /* Attribute name */ const char *alarm; /* Alarm attribute name */ }; @@ -2209,8 +2207,8 @@ static const u32 pmbus_fan_status_flags[] = { /* Precondition: FAN_CONFIG_x_y and FAN_COMMAND_x must exist for the fan ID */ static int pmbus_add_fan_ctrl(struct i2c_client *client, - struct pmbus_data *data, int index, int page, int id, - u8 config) + struct pmbus_data *data, int index, int page, + int id, u8 config) { struct pmbus_sensor *sensor; @@ -2222,7 +2220,7 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client, return -ENOMEM; if (!((data->info->func[page] & PMBUS_HAVE_PWM12) || - (data->info->func[page] & PMBUS_HAVE_PWM34))) + (data->info->func[page] & PMBUS_HAVE_PWM34))) return 0; sensor = pmbus_add_sensor(data, "pwm", NULL, index, page, @@ -2527,7 +2525,7 @@ static int pmbus_read_coefficients(struct i2c_client *client, rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS, I2C_SMBUS_BLOCK_PROC_CALL, &data); - pmbus_update_ts(client, true); + pmbus_update_ts(client, PMBUS_OP_WRITE); if (rv < 0) return rv; @@ -2665,6 +2663,56 @@ static void pmbus_remove_pec(void *dev) device_remove_file(dev, &dev_attr_pec); } +static void pmbus_init_wp(struct i2c_client *client, struct pmbus_data *data) +{ + int ret; + + switch (wp) { + case 0: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, 0); + break; + + case 1: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, PB_WP_VOUT); + break; + + case 2: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, PB_WP_OP); + break; + + case 3: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, PB_WP_ALL); + break; + + default: + /* Ignore the other values */ + break; + } + + ret = _pmbus_read_byte_data(client, -1, PMBUS_WRITE_PROTECT); + if (ret < 0) + return; + + switch (ret & PB_WP_ANY) { + case PB_WP_ALL: + data->flags |= PMBUS_OP_PROTECTED; + fallthrough; + case PB_WP_OP: + data->flags |= PMBUS_VOUT_PROTECTED; + fallthrough; + case PB_WP_VOUT: + data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; + break; + + default: + break; + } +} + static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, struct pmbus_driver_info *info) { @@ -2681,7 +2729,7 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, if (!(data->flags & PMBUS_NO_CAPABILITY)) { pmbus_wait(client); ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) @@ -2697,13 +2745,13 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, data->read_status = pmbus_read_status_word; pmbus_wait(client); ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); if (ret < 0 || ret == 0xffff) { data->read_status = pmbus_read_status_byte; pmbus_wait(client); ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); - pmbus_update_ts(client, false); + pmbus_update_ts(client, 0); if (ret < 0 || ret == 0xff) { dev_err(dev, "PMBus status register not found\n"); @@ -2718,12 +2766,8 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, * faults, and we should not try it. Also, in that case, writes into * limit registers need to be disabled. */ - if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { - ret = _pmbus_read_byte_data(client, -1, PMBUS_WRITE_PROTECT); - - if (ret > 0 && (ret & PB_WP_ANY)) - data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; - } + if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) + pmbus_init_wp(client, data); ret = i2c_smbus_read_byte_data(client, PMBUS_REVISION); if (ret >= 0) @@ -2886,7 +2930,7 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags) } static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags, - unsigned int *event, bool notify) + unsigned int *event, bool notify) { int i, status; const struct pmbus_status_category *cat; @@ -2915,7 +2959,6 @@ static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flag if (notify && status) pmbus_notify(data, page, cat->reg, status); - } /* @@ -2966,7 +3009,6 @@ static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flag *event |= REGULATOR_EVENT_OVER_TEMP_WARN; } - return 0; } @@ -3179,12 +3221,16 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, } static int pmbus_regulator_list_voltage(struct regulator_dev *rdev, - unsigned int selector) + unsigned int selector) { struct device *dev = rdev_get_dev(rdev); struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); int val, low, high; + if (data->flags & PMBUS_VOUT_PROTECTED) + return 0; + if (selector >= rdev->desc->n_voltages || selector < rdev->desc->linear_min_sel) return -EINVAL; @@ -3219,6 +3265,22 @@ const struct regulator_ops pmbus_regulator_ops = { }; EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, "PMBUS"); +int pmbus_regulator_init_cb(struct regulator_dev *rdev, + struct regulator_config *config) +{ + struct pmbus_data *data = config->driver_data; + struct regulation_constraints *constraints = rdev->constraints; + + if (data->flags & PMBUS_OP_PROTECTED) + constraints->valid_ops_mask &= ~REGULATOR_CHANGE_STATUS; + + if (data->flags & PMBUS_VOUT_PROTECTED) + constraints->valid_ops_mask &= ~REGULATOR_CHANGE_VOLTAGE; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(pmbus_regulator_init_cb, "PMBUS"); + static int pmbus_regulator_register(struct pmbus_data *data) { struct device *dev = data->dev; @@ -3251,17 +3313,16 @@ static int pmbus_regulator_register(struct pmbus_data *data) return 0; } -static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event) +static void pmbus_regulator_notify(struct pmbus_data *data, int page, int event) { - int j; + int j; - for (j = 0; j < data->info->num_regulators; j++) { - if (page == rdev_get_id(data->rdevs[j])) { - regulator_notifier_call_chain(data->rdevs[j], event, NULL); - break; - } + for (j = 0; j < data->info->num_regulators; j++) { + if (page == rdev_get_id(data->rdevs[j])) { + regulator_notifier_call_chain(data->rdevs[j], event, NULL); + break; } - return 0; + } } #else static int pmbus_regulator_register(struct pmbus_data *data) @@ -3269,9 +3330,8 @@ static int pmbus_regulator_register(struct pmbus_data *data) return 0; } -static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event) +static void pmbus_regulator_notify(struct pmbus_data *data, int page, int event) { - return 0; } #endif @@ -3294,8 +3354,8 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata) { struct pmbus_data *data = pdata; struct i2c_client *client = to_i2c_client(data->dev); - int i, status, event; + mutex_lock(&data->update_lock); for (i = 0; i < data->info->pages; i++) { _pmbus_get_flags(data, i, &status, &event, true); @@ -3359,7 +3419,6 @@ static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data) static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */ -#if IS_ENABLED(CONFIG_DEBUG_FS) static int pmbus_debugfs_get(void *data, u64 *val) { int rc; @@ -3402,8 +3461,8 @@ static int pmbus_debugfs_get_status(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status, NULL, "0x%04llx\n"); -static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) +static ssize_t pmbus_debugfs_block_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) { int rc; struct pmbus_debugfs_entry *entry = file->private_data; @@ -3428,51 +3487,98 @@ static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf, return simple_read_from_buffer(buf, count, ppos, data, rc); } -static const struct file_operations pmbus_debugfs_ops_mfr = { +static const struct file_operations pmbus_debugfs_block_ops = { .llseek = noop_llseek, - .read = pmbus_debugfs_mfr_read, + .read = pmbus_debugfs_block_read, .write = NULL, .open = simple_open, }; -static void pmbus_remove_debugfs(void *data) +static void pmbus_remove_symlink(void *symlink) { - struct dentry *entry = data; - - debugfs_remove_recursive(entry); + debugfs_remove(symlink); } -static int pmbus_init_debugfs(struct i2c_client *client, - struct pmbus_data *data) +struct pmbus_debugfs_data { + u8 reg; + u32 flag; + const char *name; +}; + +static const struct pmbus_debugfs_data pmbus_debugfs_block_data[] = { + { .reg = PMBUS_MFR_ID, .name = "mfr_id" }, + { .reg = PMBUS_MFR_MODEL, .name = "mfr_model" }, + { .reg = PMBUS_MFR_REVISION, .name = "mfr_revision" }, + { .reg = PMBUS_MFR_LOCATION, .name = "mfr_location" }, + { .reg = PMBUS_MFR_DATE, .name = "mfr_date" }, + { .reg = PMBUS_MFR_SERIAL, .name = "mfr_serial" }, +}; + +static const struct pmbus_debugfs_data pmbus_debugfs_status_data[] = { + { .reg = PMBUS_STATUS_VOUT, .flag = PMBUS_HAVE_STATUS_VOUT, .name = "status%d_vout" }, + { .reg = PMBUS_STATUS_IOUT, .flag = PMBUS_HAVE_STATUS_IOUT, .name = "status%d_iout" }, + { .reg = PMBUS_STATUS_INPUT, .flag = PMBUS_HAVE_STATUS_INPUT, .name = "status%d_input" }, + { .reg = PMBUS_STATUS_TEMPERATURE, .flag = PMBUS_HAVE_STATUS_TEMP, + .name = "status%d_temp" }, + { .reg = PMBUS_STATUS_FAN_12, .flag = PMBUS_HAVE_STATUS_FAN12, .name = "status%d_fan12" }, + { .reg = PMBUS_STATUS_FAN_34, .flag = PMBUS_HAVE_STATUS_FAN34, .name = "status%d_fan34" }, + { .reg = PMBUS_STATUS_CML, .name = "status%d_cml" }, + { .reg = PMBUS_STATUS_OTHER, .name = "status%d_other" }, + { .reg = PMBUS_STATUS_MFR_SPECIFIC, .name = "status%d_mfr" }, +}; + +static void pmbus_init_debugfs(struct i2c_client *client, + struct pmbus_data *data) { - int i, idx = 0; - char name[PMBUS_NAME_SIZE]; + struct dentry *symlink_d, *debugfs = client->debugfs; struct pmbus_debugfs_entry *entries; + const char *pathname, *symlink; + char name[PMBUS_NAME_SIZE]; + int page, i, idx = 0; - if (!pmbus_debugfs_dir) - return -ENODEV; + /* + * client->debugfs may be NULL or an ERR_PTR(). dentry_path_raw() + * does not check if its parameters are valid, so validate + * client->debugfs before using it. + */ + if (!pmbus_debugfs_dir || IS_ERR_OR_NULL(debugfs)) + return; /* - * Create the debugfs directory for this device. Use the hwmon device - * name to avoid conflicts (hwmon numbers are globally unique). + * Backwards compatibility: Create symlink from /pmbus/<hwmon_device> + * to i2c debugfs directory. */ - data->debugfs = debugfs_create_dir(dev_name(data->hwmon_dev), - pmbus_debugfs_dir); - if (IS_ERR_OR_NULL(data->debugfs)) { - data->debugfs = NULL; - return -ENODEV; - } + pathname = dentry_path_raw(debugfs, name, sizeof(name)); + if (IS_ERR(pathname)) + return; + + /* + * The path returned by dentry_path_raw() starts with '/'. Prepend it + * with ".." to get the symlink relative to the pmbus root directory. + */ + symlink = kasprintf(GFP_KERNEL, "..%s", pathname); + if (!symlink) + return; + + symlink_d = debugfs_create_symlink(dev_name(data->hwmon_dev), + pmbus_debugfs_dir, symlink); + kfree(symlink); + + devm_add_action_or_reset(data->dev, pmbus_remove_symlink, symlink_d); /* * Allocate the max possible entries we need. - * 6 entries device-specific - * 10 entries page-specific + * device specific: + * ARRAY_SIZE(pmbus_debugfs_block_data) + 2 + * page specific: + * ARRAY_SIZE(pmbus_debugfs_status_data) + 1 */ entries = devm_kcalloc(data->dev, - 6 + data->info->pages * 10, sizeof(*entries), - GFP_KERNEL); + ARRAY_SIZE(pmbus_debugfs_block_data) + 2 + + data->info->pages * (ARRAY_SIZE(pmbus_debugfs_status_data) + 1), + sizeof(*entries), GFP_KERNEL); if (!entries) - return -ENOMEM; + return; /* * Add device-specific entries. @@ -3482,175 +3588,67 @@ static int pmbus_init_debugfs(struct i2c_client *client, * assume that values of the following registers are the same for all * pages and report values only for page 0. */ - if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) { + if (!(data->flags & PMBUS_NO_CAPABILITY) && + pmbus_check_byte_register(client, 0, PMBUS_CAPABILITY)) { entries[idx].client = client; entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_ID; - debugfs_create_file("mfr_id", 0444, data->debugfs, + entries[idx].reg = PMBUS_CAPABILITY; + debugfs_create_file("capability", 0444, debugfs, &entries[idx++], - &pmbus_debugfs_ops_mfr); + &pmbus_debugfs_ops); } - - if (pmbus_check_block_register(client, 0, PMBUS_MFR_MODEL)) { + if (pmbus_check_byte_register(client, 0, PMBUS_REVISION)) { entries[idx].client = client; entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_MODEL; - debugfs_create_file("mfr_model", 0444, data->debugfs, + entries[idx].reg = PMBUS_REVISION; + debugfs_create_file("pmbus_revision", 0444, debugfs, &entries[idx++], - &pmbus_debugfs_ops_mfr); + &pmbus_debugfs_ops); } - if (pmbus_check_block_register(client, 0, PMBUS_MFR_REVISION)) { - entries[idx].client = client; - entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_REVISION; - debugfs_create_file("mfr_revision", 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops_mfr); - } + for (i = 0; i < ARRAY_SIZE(pmbus_debugfs_block_data); i++) { + const struct pmbus_debugfs_data *d = &pmbus_debugfs_block_data[i]; - if (pmbus_check_block_register(client, 0, PMBUS_MFR_LOCATION)) { - entries[idx].client = client; - entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_LOCATION; - debugfs_create_file("mfr_location", 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops_mfr); - } - - if (pmbus_check_block_register(client, 0, PMBUS_MFR_DATE)) { - entries[idx].client = client; - entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_DATE; - debugfs_create_file("mfr_date", 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops_mfr); - } - - if (pmbus_check_block_register(client, 0, PMBUS_MFR_SERIAL)) { - entries[idx].client = client; - entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_SERIAL; - debugfs_create_file("mfr_serial", 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops_mfr); + if (pmbus_check_block_register(client, 0, d->reg)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = d->reg; + debugfs_create_file(d->name, 0444, debugfs, + &entries[idx++], + &pmbus_debugfs_block_ops); + } } /* Add page specific entries */ - for (i = 0; i < data->info->pages; ++i) { + for (page = 0; page < data->info->pages; ++page) { /* Check accessibility of status register if it's not page 0 */ - if (!i || pmbus_check_status_register(client, i)) { + if (!page || pmbus_check_status_register(client, page)) { /* No need to set reg as we have special read op. */ entries[idx].client = client; - entries[idx].page = i; - scnprintf(name, PMBUS_NAME_SIZE, "status%d", i); - debugfs_create_file(name, 0444, data->debugfs, + entries[idx].page = page; + scnprintf(name, PMBUS_NAME_SIZE, "status%d", page); + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops_status); } - if (data->info->func[i] & PMBUS_HAVE_STATUS_VOUT) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_VOUT; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_vout", i); - debugfs_create_file(name, 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (data->info->func[i] & PMBUS_HAVE_STATUS_IOUT) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_IOUT; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_iout", i); - debugfs_create_file(name, 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (data->info->func[i] & PMBUS_HAVE_STATUS_INPUT) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_INPUT; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_input", i); - debugfs_create_file(name, 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (data->info->func[i] & PMBUS_HAVE_STATUS_TEMP) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_TEMPERATURE; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_temp", i); - debugfs_create_file(name, 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (pmbus_check_byte_register(client, i, PMBUS_STATUS_CML)) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_CML; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_cml", i); - debugfs_create_file(name, 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (pmbus_check_byte_register(client, i, PMBUS_STATUS_OTHER)) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_OTHER; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_other", i); - debugfs_create_file(name, 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (pmbus_check_byte_register(client, i, - PMBUS_STATUS_MFR_SPECIFIC)) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_MFR_SPECIFIC; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_mfr", i); - debugfs_create_file(name, 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN12) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_FAN_12; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan12", i); - debugfs_create_file(name, 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN34) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_FAN_34; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan34", i); - debugfs_create_file(name, 0444, data->debugfs, - &entries[idx++], - &pmbus_debugfs_ops); + for (i = 0; i < ARRAY_SIZE(pmbus_debugfs_status_data); i++) { + const struct pmbus_debugfs_data *d = + &pmbus_debugfs_status_data[i]; + + if ((data->info->func[page] & d->flag) || + (!d->flag && pmbus_check_byte_register(client, page, d->reg))) { + entries[idx].client = client; + entries[idx].page = page; + entries[idx].reg = d->reg; + scnprintf(name, PMBUS_NAME_SIZE, d->name, page); + debugfs_create_file(name, 0444, debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } } } - - return devm_add_action_or_reset(data->dev, - pmbus_remove_debugfs, data->debugfs); -} -#else -static int pmbus_init_debugfs(struct i2c_client *client, - struct pmbus_data *data) -{ - return 0; } -#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) { @@ -3722,8 +3720,8 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) data->groups[0] = &data->group; memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num); - data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, - name, data, data->groups); + data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, name, + data, data->groups); if (IS_ERR(data->hwmon_dev)) { dev_err(dev, "Failed to register hwmon device\n"); return PTR_ERR(data->hwmon_dev); @@ -3737,9 +3735,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) if (ret) return ret; - ret = pmbus_init_debugfs(client, data); - if (ret) - dev_warn(dev, "Failed to register debugfs\n"); + pmbus_init_debugfs(client, data); return 0; } @@ -3747,9 +3743,15 @@ EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, "PMBUS"); struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) { - struct pmbus_data *data = i2c_get_clientdata(client); - - return data->debugfs; + /* + * client->debugfs may be an ERR_PTR(). Returning that to + * the calling code would potentially require additional + * complexity in the calling code and otherwise add no + * value. Return NULL in that case. + */ + if (IS_ERR_OR_NULL(client->debugfs)) + return NULL; + return client->debugfs; } EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, "PMBUS"); diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c index 07fe58c24485..d902d39f49f4 100644 --- a/drivers/hwmon/pmbus/tda38640.c +++ b/drivers/hwmon/pmbus/tda38640.c @@ -15,7 +15,7 @@ #include "pmbus.h" static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = { - PMBUS_REGULATOR_ONE("vout"), + PMBUS_REGULATOR_ONE_NODE("vout"), }; struct tda38640_data { diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c new file mode 100644 index 000000000000..c13edd7e1abf --- /dev/null +++ b/drivers/hwmon/pmbus/tps25990.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2024 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/hwmon-sysfs.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#include "pmbus.h" + +#define TPS25990_READ_VAUX 0xd0 +#define TPS25990_READ_VIN_MIN 0xd1 +#define TPS25990_READ_VIN_PEAK 0xd2 +#define TPS25990_READ_IIN_PEAK 0xd4 +#define TPS25990_READ_PIN_PEAK 0xd5 +#define TPS25990_READ_TEMP_AVG 0xd6 +#define TPS25990_READ_TEMP_PEAK 0xd7 +#define TPS25990_READ_VOUT_MIN 0xda +#define TPS25990_READ_VIN_AVG 0xdc +#define TPS25990_READ_VOUT_AVG 0xdd +#define TPS25990_READ_IIN_AVG 0xde +#define TPS25990_READ_PIN_AVG 0xdf +#define TPS25990_VIREF 0xe0 +#define TPS25990_PK_MIN_AVG 0xea +#define PK_MIN_AVG_RST_PEAK BIT(7) +#define PK_MIN_AVG_RST_AVG BIT(6) +#define PK_MIN_AVG_RST_MIN BIT(5) +#define PK_MIN_AVG_AVG_CNT GENMASK(2, 0) +#define TPS25990_MFR_WRITE_PROTECT 0xf8 +#define TPS25990_UNLOCKED BIT(7) + +#define TPS25990_8B_SHIFT 2 +#define TPS25990_VIN_OVF_NUM 525100 +#define TPS25990_VIN_OVF_DIV 10163 +#define TPS25990_VIN_OVF_OFF 155 +#define TPS25990_IIN_OCF_NUM 953800 +#define TPS25990_IIN_OCF_DIV 129278 +#define TPS25990_IIN_OCF_OFF 157 + +#define PK_MIN_AVG_RST_MASK (PK_MIN_AVG_RST_PEAK | \ + PK_MIN_AVG_RST_AVG | \ + PK_MIN_AVG_RST_MIN) + +/* + * Arbitrary default Rimon value: 1kOhm + * This correspond to an overcurrent limit of 55A, close to the specified limit + * of un-stacked TPS25990 and makes further calculation easier to setup in + * sensor.conf, if necessary + */ +#define TPS25990_DEFAULT_RIMON 1000000000 + +static void tps25990_set_m(int *m, u32 rimon) +{ + u64 val = ((u64)*m) * rimon; + + /* Make sure m fits the s32 type */ + *m = DIV_ROUND_CLOSEST_ULL(val, 1000000); +} + +static int tps25990_mfr_write_protect_set(struct i2c_client *client, + u8 protect) +{ + u8 val; + + switch (protect) { + case 0: + val = 0xa2; + break; + case PB_WP_ALL: + val = 0x0; + break; + default: + return -EINVAL; + } + + return pmbus_write_byte_data(client, -1, TPS25990_MFR_WRITE_PROTECT, + val); +} + +static int tps25990_mfr_write_protect_get(struct i2c_client *client) +{ + int ret = pmbus_read_byte_data(client, -1, TPS25990_MFR_WRITE_PROTECT); + + if (ret < 0) + return ret; + + return (ret & TPS25990_UNLOCKED) ? 0 : PB_WP_ALL; +} + +static int tps25990_read_word_data(struct i2c_client *client, + int page, int phase, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VIN_PEAK); + break; + + case PMBUS_VIRT_READ_VIN_MIN: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VIN_MIN); + break; + + case PMBUS_VIRT_READ_VIN_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VIN_AVG); + break; + + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VOUT_MIN); + break; + + case PMBUS_VIRT_READ_VOUT_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VOUT_AVG); + break; + + case PMBUS_VIRT_READ_IIN_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_IIN_AVG); + break; + + case PMBUS_VIRT_READ_IIN_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_IIN_PEAK); + break; + + case PMBUS_VIRT_READ_TEMP_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_TEMP_AVG); + break; + + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_TEMP_PEAK); + break; + + case PMBUS_VIRT_READ_PIN_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_PIN_AVG); + break; + + case PMBUS_VIRT_READ_PIN_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_PIN_PEAK); + break; + + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VAUX); + break; + + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + /* + * These registers provide an 8 bits value instead of a + * 10bits one. Just shifting twice the register value is + * enough to make the sensor type conversion work, even + * if the datasheet provides different m, b and R for + * those. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + break; + ret <<= TPS25990_8B_SHIFT; + break; + + case PMBUS_VIN_OV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + break; + ret = DIV_ROUND_CLOSEST(ret * TPS25990_VIN_OVF_NUM, + TPS25990_VIN_OVF_DIV); + ret += TPS25990_VIN_OVF_OFF; + break; + + case PMBUS_IIN_OC_FAULT_LIMIT: + /* + * VIREF directly sets the over-current limit at which the eFuse + * will turn the FET off and trigger a fault. Expose it through + * this generic property instead of a manufacturer specific one. + */ + ret = pmbus_read_byte_data(client, page, TPS25990_VIREF); + if (ret < 0) + break; + ret = DIV_ROUND_CLOSEST(ret * TPS25990_IIN_OCF_NUM, + TPS25990_IIN_OCF_DIV); + ret += TPS25990_IIN_OCF_OFF; + break; + + case PMBUS_VIRT_SAMPLES: + ret = pmbus_read_byte_data(client, page, TPS25990_PK_MIN_AVG); + if (ret < 0) + break; + ret = 1 << FIELD_GET(PK_MIN_AVG_AVG_CNT, ret); + break; + + case PMBUS_VIRT_RESET_TEMP_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + case PMBUS_VIRT_RESET_IIN_HISTORY: + case PMBUS_VIRT_RESET_PIN_HISTORY: + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = 0; + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int tps25990_write_word_data(struct i2c_client *client, + int page, int reg, u16 value) +{ + int ret; + + switch (reg) { + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + value >>= TPS25990_8B_SHIFT; + value = clamp_val(value, 0, 0xff); + ret = pmbus_write_word_data(client, page, reg, value); + break; + + case PMBUS_VIN_OV_FAULT_LIMIT: + value -= TPS25990_VIN_OVF_OFF; + value = DIV_ROUND_CLOSEST(((unsigned int)value) * TPS25990_VIN_OVF_DIV, + TPS25990_VIN_OVF_NUM); + value = clamp_val(value, 0, 0xf); + ret = pmbus_write_word_data(client, page, reg, value); + break; + + case PMBUS_IIN_OC_FAULT_LIMIT: + value -= TPS25990_IIN_OCF_OFF; + value = DIV_ROUND_CLOSEST(((unsigned int)value) * TPS25990_IIN_OCF_DIV, + TPS25990_IIN_OCF_NUM); + value = clamp_val(value, 0, 0x3f); + ret = pmbus_write_byte_data(client, page, TPS25990_VIREF, value); + break; + + case PMBUS_VIRT_SAMPLES: + value = clamp_val(value, 1, 1 << PK_MIN_AVG_AVG_CNT); + value = ilog2(value); + ret = pmbus_update_byte_data(client, page, TPS25990_PK_MIN_AVG, + PK_MIN_AVG_AVG_CNT, + FIELD_PREP(PK_MIN_AVG_AVG_CNT, value)); + break; + + case PMBUS_VIRT_RESET_TEMP_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + case PMBUS_VIRT_RESET_IIN_HISTORY: + case PMBUS_VIRT_RESET_PIN_HISTORY: + case PMBUS_VIRT_RESET_VOUT_HISTORY: + /* + * TPS25990 has history resets based on MIN/AVG/PEAK instead of per + * sensor type. Exposing this quirk in hwmon is not desirable so + * reset MIN, AVG and PEAK together. Even is there effectively only + * one reset, which resets everything, expose the 5 entries so + * userspace is not required map a sensor type to another to trigger + * a reset + */ + ret = pmbus_update_byte_data(client, 0, TPS25990_PK_MIN_AVG, + PK_MIN_AVG_RST_MASK, + PK_MIN_AVG_RST_MASK); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int tps25990_read_byte_data(struct i2c_client *client, + int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_WRITE_PROTECT: + ret = tps25990_mfr_write_protect_get(client); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int tps25990_write_byte_data(struct i2c_client *client, + int page, int reg, u8 byte) +{ + int ret; + + switch (reg) { + case PMBUS_WRITE_PROTECT: + ret = tps25990_mfr_write_protect_set(client, byte); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR) +static const struct regulator_desc tps25990_reg_desc[] = { + PMBUS_REGULATOR_ONE_NODE("vout"), +}; +#endif + +static const struct pmbus_driver_info tps25990_base_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = direct, + .m[PSC_VOLTAGE_IN] = 5251, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -2, + .format[PSC_VOLTAGE_OUT] = direct, + .m[PSC_VOLTAGE_OUT] = 5251, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = -2, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_TEMPERATURE] = 140, + .b[PSC_TEMPERATURE] = 32100, + .R[PSC_TEMPERATURE] = -2, + /* + * Current and Power measurement depends on the ohm value + * of Rimon. m is multiplied by 1000 below to have an integer + * and -3 is added to R to compensate. + */ + .format[PSC_CURRENT_IN] = direct, + .m[PSC_CURRENT_IN] = 9538, + .b[PSC_CURRENT_IN] = 0, + .R[PSC_CURRENT_IN] = -6, + .format[PSC_POWER] = direct, + .m[PSC_POWER] = 4901, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = -7, + .func[0] = (PMBUS_HAVE_VIN | + PMBUS_HAVE_VOUT | + PMBUS_HAVE_VMON | + PMBUS_HAVE_IIN | + PMBUS_HAVE_PIN | + PMBUS_HAVE_TEMP | + PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_SAMPLES), + .read_word_data = tps25990_read_word_data, + .write_word_data = tps25990_write_word_data, + .read_byte_data = tps25990_read_byte_data, + .write_byte_data = tps25990_write_byte_data, + +#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR) + .reg_desc = tps25990_reg_desc, + .num_regulators = ARRAY_SIZE(tps25990_reg_desc), +#endif +}; + +static const struct i2c_device_id tps25990_i2c_id[] = { + { "tps25990" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tps25990_i2c_id); + +static const struct of_device_id tps25990_of_match[] = { + { .compatible = "ti,tps25990" }, + {} +}; +MODULE_DEVICE_TABLE(of, tps25990_of_match); + +static int tps25990_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pmbus_driver_info *info; + u32 rimon = TPS25990_DEFAULT_RIMON; + int ret; + + ret = device_property_read_u32(dev, "ti,rimon-micro-ohms", &rimon); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, "failed to get rimon\n"); + + info = devm_kmemdup(dev, &tps25990_base_info, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + /* Adapt the current and power scale for each instance */ + tps25990_set_m(&info->m[PSC_CURRENT_IN], rimon); + tps25990_set_m(&info->m[PSC_POWER], rimon); + + return pmbus_do_probe(client, info); +} + +static struct i2c_driver tps25990_driver = { + .driver = { + .name = "tps25990", + .of_match_table = tps25990_of_match, + }, + .probe = tps25990_probe, + .id_table = tps25990_i2c_id, +}; +module_i2c_driver(tps25990_driver); + +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_DESCRIPTION("PMBUS driver for TPS25990 eFuse"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 9b0eadc81a2e..52d4000902d5 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -212,8 +212,8 @@ static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset) return !!(ret & UCD9000_GPIO_CONFIG_STATUS); } -static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, - int value) +static int ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct i2c_client *client = gpiochip_get_data(gc); int ret; @@ -222,19 +222,19 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, if (ret < 0) { dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n", offset, ret); - return; + return ret; } if (value) { - if (ret & UCD9000_GPIO_CONFIG_STATUS) - return; + if (ret & UCD9000_GPIO_CONFIG_OUT_VALUE) + return 0; - ret |= UCD9000_GPIO_CONFIG_STATUS; + ret |= UCD9000_GPIO_CONFIG_OUT_VALUE; } else { - if (!(ret & UCD9000_GPIO_CONFIG_STATUS)) - return; + if (!(ret & UCD9000_GPIO_CONFIG_OUT_VALUE)) + return 0; - ret &= ~UCD9000_GPIO_CONFIG_STATUS; + ret &= ~UCD9000_GPIO_CONFIG_OUT_VALUE; } ret |= UCD9000_GPIO_CONFIG_ENABLE; @@ -244,7 +244,7 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, if (ret < 0) { dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n", offset, ret); - return; + return ret; } ret &= ~UCD9000_GPIO_CONFIG_ENABLE; @@ -253,6 +253,8 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, if (ret < 0) dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n", offset, ret); + + return ret; } static int ucd9000_gpio_get_direction(struct gpio_chip *gc, @@ -362,7 +364,7 @@ static void ucd9000_probe_gpio(struct i2c_client *client, data->gpio.direction_input = ucd9000_gpio_direction_input; data->gpio.direction_output = ucd9000_gpio_direction_output; data->gpio.get = ucd9000_gpio_get; - data->gpio.set = ucd9000_gpio_set; + data->gpio.set_rv = ucd9000_gpio_set; data->gpio.can_sleep = true; data->gpio.base = -1; data->gpio.parent = &client->dev; |