summaryrefslogtreecommitdiff
path: root/drivers/hwmon/pmbus
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon/pmbus')
-rw-r--r--drivers/hwmon/pmbus/Kconfig93
-rw-r--r--drivers/hwmon/pmbus/Makefile8
-rw-r--r--drivers/hwmon/pmbus/acbel-fsg032.c2
-rw-r--r--drivers/hwmon/pmbus/adm1266.c4
-rw-r--r--drivers/hwmon/pmbus/adm1275.c11
-rw-r--r--drivers/hwmon/pmbus/adp1050.c56
-rw-r--r--drivers/hwmon/pmbus/bel-pfe.c2
-rw-r--r--drivers/hwmon/pmbus/bpa-rs600.c2
-rw-r--r--drivers/hwmon/pmbus/crps.c74
-rw-r--r--drivers/hwmon/pmbus/delta-ahe50dc-fan.c2
-rw-r--r--drivers/hwmon/pmbus/dps920ab.c9
-rw-r--r--drivers/hwmon/pmbus/fsp-3y.c2
-rw-r--r--drivers/hwmon/pmbus/ibm-cffps.c2
-rw-r--r--drivers/hwmon/pmbus/inspur-ipsps.c4
-rw-r--r--drivers/hwmon/pmbus/ir35221.c4
-rw-r--r--drivers/hwmon/pmbus/ir36021.c4
-rw-r--r--drivers/hwmon/pmbus/ir38064.c10
-rw-r--r--drivers/hwmon/pmbus/irps5401.c4
-rw-r--r--drivers/hwmon/pmbus/isl68137.c212
-rw-r--r--drivers/hwmon/pmbus/lm25066.c4
-rw-r--r--drivers/hwmon/pmbus/lt7182s.c4
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c22
-rw-r--r--drivers/hwmon/pmbus/ltc3815.c4
-rw-r--r--drivers/hwmon/pmbus/ltc4286.c4
-rw-r--r--drivers/hwmon/pmbus/max15301.c99
-rw-r--r--drivers/hwmon/pmbus/max16064.c4
-rw-r--r--drivers/hwmon/pmbus/max16601.c2
-rw-r--r--drivers/hwmon/pmbus/max20730.c2
-rw-r--r--drivers/hwmon/pmbus/max20751.c4
-rw-r--r--drivers/hwmon/pmbus/max31785.c8
-rw-r--r--drivers/hwmon/pmbus/max34440.c2
-rw-r--r--drivers/hwmon/pmbus/max8688.c4
-rw-r--r--drivers/hwmon/pmbus/mp2856.c10
-rw-r--r--drivers/hwmon/pmbus/mp2888.c4
-rw-r--r--drivers/hwmon/pmbus/mp2891.c600
-rw-r--r--drivers/hwmon/pmbus/mp2975.c138
-rw-r--r--drivers/hwmon/pmbus/mp2993.c261
-rw-r--r--drivers/hwmon/pmbus/mp5023.c2
-rw-r--r--drivers/hwmon/pmbus/mp5920.c90
-rw-r--r--drivers/hwmon/pmbus/mp5990.c4
-rw-r--r--drivers/hwmon/pmbus/mp9941.c319
-rw-r--r--drivers/hwmon/pmbus/mpq7932.c4
-rw-r--r--drivers/hwmon/pmbus/mpq8785.c6
-rw-r--r--drivers/hwmon/pmbus/pim4328.c2
-rw-r--r--drivers/hwmon/pmbus/pli1209bc.c30
-rw-r--r--drivers/hwmon/pmbus/pm6764tr.c4
-rw-r--r--drivers/hwmon/pmbus/pmbus.c2
-rw-r--r--drivers/hwmon/pmbus/pmbus.h20
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c253
-rw-r--r--drivers/hwmon/pmbus/pxe1610.c8
-rw-r--r--drivers/hwmon/pmbus/q54sj108a2.c2
-rw-r--r--drivers/hwmon/pmbus/stpddc60.c6
-rw-r--r--drivers/hwmon/pmbus/tda38640.c4
-rw-r--r--drivers/hwmon/pmbus/tps25990.c436
-rw-r--r--drivers/hwmon/pmbus/tps40422.c4
-rw-r--r--drivers/hwmon/pmbus/tps53679.c2
-rw-r--r--drivers/hwmon/pmbus/tps546d24.c4
-rw-r--r--drivers/hwmon/pmbus/ucd9000.c72
-rw-r--r--drivers/hwmon/pmbus/ucd9200.c2
-rw-r--r--drivers/hwmon/pmbus/xdp710.c131
-rw-r--r--drivers/hwmon/pmbus/xdpe12284.c8
-rw-r--r--drivers/hwmon/pmbus/xdpe152c4.c6
-rw-r--r--drivers/hwmon/pmbus/zl6100.c68
63 files changed, 2757 insertions, 413 deletions
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 557ae0c414b0..419469f40ba0 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -51,12 +51,22 @@ 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, ADM1293,
- and ADM1294 Hot-Swap Controller and Digital Power Monitors.
+ 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
be called adm1275.
+config SENSORS_ADP1050
+ tristate "Analog Devices ADP1050 digital controller for Power Supplies"
+ help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices ADP1050 digital controller for isolated power supply with
+ PMBus interface.
+
+ This driver can also be built as a module. If so, the module will
+ be called adp1050.
+
config SENSORS_BEL_PFE
tristate "Bel PFE Compatible Power Supplies"
help
@@ -75,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
@@ -214,9 +233,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, LTC7880,
- LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, LTM4686,
- and LTM4700.
+ LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7841,
+ LTC7880, LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680,
+ LTM4686, and LTM4700.
config SENSORS_LTC3815
tristate "Linear Technologies LTC3815"
@@ -241,7 +260,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.
@@ -327,6 +346,15 @@ config SENSORS_MP2888
This driver can also be built as a module. If so, the module will
be called mp2888.
+config SENSORS_MP2891
+ tristate "MPS MP2891"
+ help
+ If you say yes here you get hardware monitoring support for MPS
+ MP2891 Dual Loop Digital Multi-Phase Controller.
+
+ This driver can also be built as a module. If so, the module will
+ be called mp2891.
+
config SENSORS_MP2975
tristate "MPS MP2975"
help
@@ -336,6 +364,15 @@ config SENSORS_MP2975
This driver can also be built as a module. If so, the module will
be called mp2975.
+config SENSORS_MP2993
+ tristate "MPS MP2993"
+ help
+ If you say yes here you get hardware monitoring support for MPS
+ MP2993 Dual Loop Digital Multi-Phase Controller.
+
+ This driver can also be built as a module. If so, the module will
+ be called mp2993.
+
config SENSORS_MP2975_REGULATOR
depends on SENSORS_MP2975 && REGULATOR
bool "Regulator support for MPS MP2975"
@@ -352,6 +389,15 @@ config SENSORS_MP5023
This driver can also be built as a module. If so, the module will
be called mp5023.
+config SENSORS_MP5920
+ tristate "MPS MP5920"
+ help
+ If you say yes here you get hardware monitoring support for Monolithic
+ MP5920.
+
+ This driver can also be built as a module. If so, the module will
+ be called mp5920.
+
config SENSORS_MP5990
tristate "MPS MP5990"
help
@@ -361,6 +407,15 @@ config SENSORS_MP5990
This driver can also be built as a module. If so, the module will
be called mp5990.
+config SENSORS_MP9941
+ tristate "MPS MP9941"
+ help
+ If you say yes here you get hardware monitoring support for MPS
+ MP9941.
+
+ This driver can also be built as a module. If so, the module will
+ be called mp9941.
+
config SENSORS_MPQ7932_REGULATOR
bool "Regulator support for MPQ7932"
depends on SENSORS_MPQ7932 && REGULATOR
@@ -464,6 +519,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
@@ -511,6 +583,15 @@ config SENSORS_UCD9200
This driver can also be built as a module. If so, the module will
be called ucd9200.
+config SENSORS_XDP710
+ tristate "Infineon XDP710 family"
+ help
+ If you say yes here you get hardware monitoring support for Infineon
+ XDP710.
+
+ This driver can also be built as a module. If so, the module will
+ be called xdp710.
+
config SENSORS_XDPE152
tristate "Infineon XDPE152 family"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index f14ecf03ad77..c7eb7739b7f8 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
obj-$(CONFIG_SENSORS_ACBEL_FSG032) += acbel-fsg032.o
obj-$(CONFIG_SENSORS_ADM1266) += adm1266.o
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
+obj-$(CONFIG_SENSORS_ADP1050) += adp1050.o
obj-$(CONFIG_SENSORS_BEL_PFE) += bel-pfe.o
obj-$(CONFIG_SENSORS_BPA_RS600) += bpa-rs600.o
obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o
@@ -35,9 +36,13 @@ obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
obj-$(CONFIG_SENSORS_MP2856) += mp2856.o
obj-$(CONFIG_SENSORS_MP2888) += mp2888.o
+obj-$(CONFIG_SENSORS_MP2891) += mp2891.o
obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
+obj-$(CONFIG_SENSORS_MP2993) += mp2993.o
obj-$(CONFIG_SENSORS_MP5023) += mp5023.o
+obj-$(CONFIG_SENSORS_MP5920) += mp5920.o
obj-$(CONFIG_SENSORS_MP5990) += mp5990.o
+obj-$(CONFIG_SENSORS_MP9941) += mp9941.o
obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o
obj-$(CONFIG_SENSORS_MPQ8785) += mpq8785.o
obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o
@@ -46,12 +51,15 @@ 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
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
+obj-$(CONFIG_SENSORS_XDP710) += xdp710.o
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/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c
index e0c55fd8f3a6..9f07fb4abaff 100644
--- a/drivers/hwmon/pmbus/acbel-fsg032.c
+++ b/drivers/hwmon/pmbus/acbel-fsg032.c
@@ -120,4 +120,4 @@ module_i2c_driver(acbel_fsg032_driver);
MODULE_AUTHOR("Lakshmi Yadlapati");
MODULE_DESCRIPTION("PMBus driver for AcBel Power System power supplies");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index ed0a7b9fae4b..d90f8f80be8e 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -490,7 +490,7 @@ static const struct of_device_id adm1266_of_match[] = {
MODULE_DEVICE_TABLE(of, adm1266_of_match);
static const struct i2c_device_id adm1266_id[] = {
- { "adm1266", 0 },
+ { "adm1266" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1266_id);
@@ -509,4 +509,4 @@ module_i2c_driver(adm1266_driver);
MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>");
MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1266");
MODULE_LICENSE("GPL v2");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index e2c61d6fa521..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, 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,9 +479,11 @@ 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 },
+ { "adm1281", adm1281 },
{ "adm1293", adm1293 },
{ "adm1294", adm1294 },
{ }
@@ -554,7 +556,8 @@ 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 ||
+ 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
@@ -628,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;
@@ -703,6 +707,7 @@ static int adm1275_probe(struct i2c_client *client)
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
break;
case adm1278:
+ case adm1281:
data->have_vout = true;
data->have_pin_max = true;
data->have_temp_max = true;
@@ -863,4 +868,4 @@ module_i2c_driver(adm1275_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/adp1050.c b/drivers/hwmon/pmbus/adp1050.c
new file mode 100644
index 000000000000..ef46c880b168
--- /dev/null
+++ b/drivers/hwmon/pmbus/adp1050.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Hardware monitoring driver for Analog Devices ADP1050
+ *
+ * Copyright (C) 2024 Analog Devices, Inc.
+ */
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+
+#include "pmbus.h"
+
+static struct pmbus_driver_info adp1050_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_VOLTAGE_OUT] = linear,
+ .format[PSC_CURRENT_IN] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_IIN | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_STATUS_TEMP,
+};
+
+static int adp1050_probe(struct i2c_client *client)
+{
+ return pmbus_do_probe(client, &adp1050_info);
+}
+
+static const struct i2c_device_id adp1050_id[] = {
+ {"adp1050"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adp1050_id);
+
+static const struct of_device_id adp1050_of_match[] = {
+ { .compatible = "adi,adp1050"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, adp1050_of_match);
+
+static struct i2c_driver adp1050_driver = {
+ .driver = {
+ .name = "adp1050",
+ .of_match_table = adp1050_of_match,
+ },
+ .probe = adp1050_probe,
+ .id_table = adp1050_id,
+};
+module_i2c_driver(adp1050_driver);
+
+MODULE_AUTHOR("Radu Sabau <radu.sabau@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADP1050 HWMON PMBus Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c
index 7c5f4b10a7c1..ddf9d9a2958c 100644
--- a/drivers/hwmon/pmbus/bel-pfe.c
+++ b/drivers/hwmon/pmbus/bel-pfe.c
@@ -129,4 +129,4 @@ module_i2c_driver(pfe_pmbus_driver);
MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>");
MODULE_DESCRIPTION("PMBus driver for BEL PFE Family Power Supplies");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c
index 0dce26c35556..6c3875ba37a0 100644
--- a/drivers/hwmon/pmbus/bpa-rs600.c
+++ b/drivers/hwmon/pmbus/bpa-rs600.c
@@ -205,4 +205,4 @@ module_i2c_driver(bpa_rs600_driver);
MODULE_AUTHOR("Chris Packham");
MODULE_DESCRIPTION("PMBus driver for BluTek BPA-RS600");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
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/delta-ahe50dc-fan.c b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c
index 4dd3b6686d6a..3850eaea75da 100644
--- a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c
+++ b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c
@@ -127,4 +127,4 @@ module_i2c_driver(ahe50dc_fan_driver);
MODULE_AUTHOR("Zev Weiss <zev@bewilderbeest.net>");
MODULE_DESCRIPTION("Driver for Delta AHE-50DC power shelf fan control module");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c
index 04e0d598a6e5..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);
@@ -203,4 +210,4 @@ module_i2c_driver(dps920ab_driver);
MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");
MODULE_DESCRIPTION("PMBus driver for Delta DPS920AB PSU");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c
index 72a7c261ef06..a4dc09e2ef75 100644
--- a/drivers/hwmon/pmbus/fsp-3y.c
+++ b/drivers/hwmon/pmbus/fsp-3y.c
@@ -291,4 +291,4 @@ module_i2c_driver(fsp3y_driver);
MODULE_AUTHOR("Václav Kubernát");
MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 1ba4c5e95820..d05ef7a968a9 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -614,4 +614,4 @@ module_i2c_driver(ibm_cffps_driver);
MODULE_AUTHOR("Eddie James");
MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c
index dfeae68b5e52..074e0f164ee1 100644
--- a/drivers/hwmon/pmbus/inspur-ipsps.c
+++ b/drivers/hwmon/pmbus/inspur-ipsps.c
@@ -197,7 +197,7 @@ static int ipsps_probe(struct i2c_client *client)
}
static const struct i2c_device_id ipsps_id[] = {
- { "ipsps1", 0 },
+ { "ipsps1" },
{}
};
MODULE_DEVICE_TABLE(i2c, ipsps_id);
@@ -224,4 +224,4 @@ module_i2c_driver(ipsps_driver);
MODULE_AUTHOR("John Wang");
MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c
index e3ee5c1bd967..46d8f334d49a 100644
--- a/drivers/hwmon/pmbus/ir35221.c
+++ b/drivers/hwmon/pmbus/ir35221.c
@@ -126,7 +126,7 @@ static int ir35221_probe(struct i2c_client *client)
}
static const struct i2c_device_id ir35221_id[] = {
- {"ir35221", 0},
+ {"ir35221"},
{}
};
@@ -145,4 +145,4 @@ module_i2c_driver(ir35221_driver);
MODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com");
MODULE_DESCRIPTION("PMBus driver for IR35221");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c
index a263afeb8ac1..34ce15fc708b 100644
--- a/drivers/hwmon/pmbus/ir36021.c
+++ b/drivers/hwmon/pmbus/ir36021.c
@@ -51,7 +51,7 @@ static int ir36021_probe(struct i2c_client *client)
}
static const struct i2c_device_id ir36021_id[] = {
- { "ir36021", 0 },
+ { "ir36021" },
{},
};
MODULE_DEVICE_TABLE(i2c, ir36021_id);
@@ -76,4 +76,4 @@ module_i2c_driver(ir36021_driver);
MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>");
MODULE_DESCRIPTION("PMBus driver for Infineon IR36021");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c
index 69e18cb468f6..7b4188e8bf48 100644
--- a/drivers/hwmon/pmbus/ir38064.c
+++ b/drivers/hwmon/pmbus/ir38064.c
@@ -53,10 +53,10 @@ static int ir38064_probe(struct i2c_client *client)
}
static const struct i2c_device_id ir38064_id[] = {
- {"ir38060", 0},
- {"ir38064", 0},
- {"ir38164", 0},
- {"ir38263", 0},
+ {"ir38060"},
+ {"ir38064"},
+ {"ir38164"},
+ {"ir38263"},
{}
};
@@ -87,4 +87,4 @@ module_i2c_driver(ir38064_driver);
MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
MODULE_DESCRIPTION("PMBus driver for Infineon IR38064 and compatible chips");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c
index 146d32a35a7c..43674c64841d 100644
--- a/drivers/hwmon/pmbus/irps5401.c
+++ b/drivers/hwmon/pmbus/irps5401.c
@@ -44,7 +44,7 @@ static int irps5401_probe(struct i2c_client *client)
}
static const struct i2c_device_id irps5401_id[] = {
- {"irps5401", 0},
+ {"irps5401"},
{}
};
@@ -63,4 +63,4 @@ module_i2c_driver(irps5401_driver);
MODULE_AUTHOR("Robert Hancock");
MODULE_DESCRIPTION("PMBus driver for Infineon IRPS5401");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c
index 7e53fb1d5ea3..2af921039309 100644
--- a/drivers/hwmon/pmbus/isl68137.c
+++ b/drivers/hwmon/pmbus/isl68137.c
@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/string.h>
#include <linux/sysfs.h>
@@ -20,6 +21,7 @@
#define ISL68137_VOUT_AVS 0x30
#define RAA_DMPVR2_READ_VMON 0xc8
+#define MAX_CHANNELS 4
enum chips {
isl68137,
@@ -72,6 +74,17 @@ enum variants {
raa_dmpvr2_hv,
};
+struct isl68137_channel {
+ u32 vout_voltage_divider[2];
+};
+
+struct isl68137_data {
+ struct pmbus_driver_info info;
+ struct isl68137_channel channel[MAX_CHANNELS];
+};
+
+#define to_isl68137_data(x) container_of(x, struct isl68137_data, info)
+
static const struct i2c_device_id raa_dmpvr_id[];
static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client,
@@ -163,13 +176,32 @@ static const struct attribute_group *isl68137_attribute_groups[] = {
static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct isl68137_data *data = to_isl68137_data(info);
int ret;
+ u64 temp;
switch (reg) {
case PMBUS_VIRT_READ_VMON:
ret = pmbus_read_word_data(client, page, phase,
RAA_DMPVR2_READ_VMON);
break;
+ case PMBUS_READ_POUT:
+ case PMBUS_READ_VOUT:
+ /*
+ * In cases where a voltage divider is attached to the target
+ * rail between Vout and the Vsense pin, both Vout and Pout
+ * should be scaled by the voltage divider scaling factor.
+ * I.e. Vout = Vsense * Rtotal / Rout
+ */
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret > 0) {
+ temp = DIV_U64_ROUND_CLOSEST((u64)ret *
+ data->channel[page].vout_voltage_divider[1],
+ data->channel[page].vout_voltage_divider[0]);
+ ret = clamp_val(temp, 0, 0xffff);
+ }
+ break;
default:
ret = -ENODATA;
break;
@@ -178,6 +210,40 @@ static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page,
return ret;
}
+static int raa_dmpvr2_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct isl68137_data *data = to_isl68137_data(info);
+ int ret;
+ u64 temp;
+
+ switch (reg) {
+ case PMBUS_VOUT_MAX:
+ case PMBUS_VOUT_MARGIN_HIGH:
+ case PMBUS_VOUT_MARGIN_LOW:
+ case PMBUS_VOUT_OV_FAULT_LIMIT:
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ case PMBUS_VOUT_COMMAND:
+ /*
+ * In cases where a voltage divider is attached to the target
+ * rail between Vout and the Vsense pin, Vout related PMBus
+ * commands should be scaled based on the expected voltage
+ * at the Vsense pin.
+ * I.e. Vsense = Vout * Rout / Rtotal
+ */
+ temp = DIV_U64_ROUND_CLOSEST((u64)word *
+ data->channel[page].vout_voltage_divider[0],
+ data->channel[page].vout_voltage_divider[1]);
+ ret = clamp_val(temp, 0, 0xffff);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
static struct pmbus_driver_info raa_dmpvr_info = {
.pages = 3,
.format[PSC_VOLTAGE_IN] = direct,
@@ -220,14 +286,90 @@ static struct pmbus_driver_info raa_dmpvr_info = {
| PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT,
};
+static int isl68137_probe_child_from_dt(struct device *dev,
+ struct device_node *child,
+ struct isl68137_data *data)
+{
+ u32 channel, rout, rtotal;
+ int err;
+
+ err = of_property_read_u32(child, "reg", &channel);
+ if (err) {
+ dev_err(dev, "missing reg property of %pOFn\n", child);
+ return err;
+ }
+ if (channel >= data->info.pages) {
+ dev_err(dev, "invalid reg %d of %pOFn\n", channel, child);
+ return -EINVAL;
+ }
+
+ err = of_property_read_u32_array(child, "vout-voltage-divider",
+ data->channel[channel].vout_voltage_divider,
+ ARRAY_SIZE(data->channel[channel].vout_voltage_divider));
+ if (err && err != -EINVAL) {
+ dev_err(dev,
+ "malformed vout-voltage-divider value for channel %d\n",
+ channel);
+ return err;
+ }
+
+ rout = data->channel[channel].vout_voltage_divider[0];
+ rtotal = data->channel[channel].vout_voltage_divider[1];
+ if (rout == 0) {
+ dev_err(dev,
+ "Voltage divider output resistance must be greater than 0\n");
+ return -EINVAL;
+ }
+ if (rtotal < rout) {
+ dev_err(dev,
+ "Voltage divider total resistance is less than output resistance\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int isl68137_probe_from_dt(struct device *dev,
+ struct isl68137_data *data)
+{
+ const struct device_node *np = dev->of_node;
+ struct device_node *child;
+ int err;
+
+ for_each_child_of_node(np, child) {
+ if (strcmp(child->name, "channel"))
+ continue;
+
+ err = isl68137_probe_child_from_dt(dev, child, data);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int isl68137_probe(struct i2c_client *client)
{
+ struct device *dev = &client->dev;
struct pmbus_driver_info *info;
+ struct isl68137_data *data;
+ int i, err;
- info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
return -ENOMEM;
- memcpy(info, &raa_dmpvr_info, sizeof(*info));
+
+ /*
+ * Initialize all voltage dividers to Rout=1 and Rtotal=1 to simplify
+ * logic in PMBus word read/write functions
+ */
+ for (i = 0; i < MAX_CHANNELS; i++)
+ memset(data->channel[i].vout_voltage_divider,
+ 1,
+ sizeof(data->channel[i].vout_voltage_divider));
+
+ memcpy(&data->info, &raa_dmpvr_info, sizeof(data->info));
+ info = &data->info;
switch (i2c_match_id(raa_dmpvr_id, client)->driver_data) {
case raa_dmpvr1_2rail:
@@ -237,11 +379,14 @@ static int isl68137_probe(struct i2c_client *client)
info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_POUT;
+ info->read_word_data = raa_dmpvr2_read_word_data;
+ info->write_word_data = raa_dmpvr2_write_word_data;
info->groups = isl68137_attribute_groups;
break;
case raa_dmpvr2_1rail:
info->pages = 1;
info->read_word_data = raa_dmpvr2_read_word_data;
+ info->write_word_data = raa_dmpvr2_write_word_data;
break;
case raa_dmpvr2_2rail_nontc:
info->func[0] &= ~PMBUS_HAVE_TEMP3;
@@ -250,9 +395,11 @@ static int isl68137_probe(struct i2c_client *client)
case raa_dmpvr2_2rail:
info->pages = 2;
info->read_word_data = raa_dmpvr2_read_word_data;
+ info->write_word_data = raa_dmpvr2_write_word_data;
break;
case raa_dmpvr2_3rail:
info->read_word_data = raa_dmpvr2_read_word_data;
+ info->write_word_data = raa_dmpvr2_write_word_data;
break;
case raa_dmpvr2_hv:
info->pages = 1;
@@ -263,11 +410,16 @@ static int isl68137_probe(struct i2c_client *client)
info->m[PSC_POWER] = 2;
info->R[PSC_POWER] = -1;
info->read_word_data = raa_dmpvr2_read_word_data;
+ info->write_word_data = raa_dmpvr2_write_word_data;
break;
default:
return -ENODEV;
}
+ err = isl68137_probe_from_dt(dev, data);
+ if (err)
+ return err;
+
return pmbus_do_probe(client, info);
}
@@ -318,11 +470,59 @@ static const struct i2c_device_id raa_dmpvr_id[] = {
MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id);
+static const struct of_device_id isl68137_of_match[] = {
+ { .compatible = "isil,isl68137", .data = (void *)raa_dmpvr1_2rail },
+ { .compatible = "renesas,isl68220", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl68221", .data = (void *)raa_dmpvr2_3rail },
+ { .compatible = "renesas,isl68222", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl68223", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl68224", .data = (void *)raa_dmpvr2_3rail },
+ { .compatible = "renesas,isl68225", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl68226", .data = (void *)raa_dmpvr2_3rail },
+ { .compatible = "renesas,isl68227", .data = (void *)raa_dmpvr2_1rail },
+ { .compatible = "renesas,isl68229", .data = (void *)raa_dmpvr2_3rail },
+ { .compatible = "renesas,isl68233", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl68239", .data = (void *)raa_dmpvr2_3rail },
+
+ { .compatible = "renesas,isl69222", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69223", .data = (void *)raa_dmpvr2_3rail },
+ { .compatible = "renesas,isl69224", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69225", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69227", .data = (void *)raa_dmpvr2_3rail },
+ { .compatible = "renesas,isl69228", .data = (void *)raa_dmpvr2_3rail },
+ { .compatible = "renesas,isl69234", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69236", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69239", .data = (void *)raa_dmpvr2_3rail },
+ { .compatible = "renesas,isl69242", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69243", .data = (void *)raa_dmpvr2_1rail },
+ { .compatible = "renesas,isl69247", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69248", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69254", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69255", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69256", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69259", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "isil,isl69260", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,isl69268", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "isil,isl69269", .data = (void *)raa_dmpvr2_3rail },
+ { .compatible = "renesas,isl69298", .data = (void *)raa_dmpvr2_2rail },
+
+ { .compatible = "renesas,raa228000", .data = (void *)raa_dmpvr2_hv },
+ { .compatible = "renesas,raa228004", .data = (void *)raa_dmpvr2_hv },
+ { .compatible = "renesas,raa228006", .data = (void *)raa_dmpvr2_hv },
+ { .compatible = "renesas,raa228228", .data = (void *)raa_dmpvr2_2rail_nontc },
+ { .compatible = "renesas,raa229001", .data = (void *)raa_dmpvr2_2rail },
+ { .compatible = "renesas,raa229004", .data = (void *)raa_dmpvr2_2rail },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, isl68137_of_match);
+
/* This is the driver that will be inserted */
static struct i2c_driver isl68137_driver = {
.driver = {
- .name = "isl68137",
- },
+ .name = "isl68137",
+ .of_match_table = isl68137_of_match,
+ },
.probe = isl68137_probe,
.id_table = raa_dmpvr_id,
};
@@ -332,4 +532,4 @@ module_i2c_driver(isl68137_driver);
MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
index cfffa4cdc0df..40b0dda32ea6 100644
--- a/drivers/hwmon/pmbus/lm25066.c
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -17,7 +17,7 @@
#include <linux/of.h>
#include "pmbus.h"
-enum chips { lm25056 = 1, lm25066, lm5064, lm5066, lm5066i };
+enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i };
#define LM25066_READ_VAUX 0xd0
#define LM25066_MFR_READ_IIN 0xd1
@@ -569,4 +569,4 @@ module_i2c_driver(lm25066_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/lt7182s.c b/drivers/hwmon/pmbus/lt7182s.c
index 28afc5f15ae8..9d6d50f39bd6 100644
--- a/drivers/hwmon/pmbus/lt7182s.c
+++ b/drivers/hwmon/pmbus/lt7182s.c
@@ -168,7 +168,7 @@ static int lt7182s_probe(struct i2c_client *client)
}
static const struct i2c_device_id lt7182s_id[] = {
- { "lt7182s", 0 },
+ { "lt7182s" },
{}
};
MODULE_DEVICE_TABLE(i2c, lt7182s_id);
@@ -192,4 +192,4 @@ module_i2c_driver(lt7182s_driver);
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
MODULE_DESCRIPTION("PMBus driver for Analog Devices LT7182S");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index 73a86f4d6472..4c306943383a 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -23,7 +23,8 @@ enum chips {
/* Managers */
ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980,
/* Controllers */
- ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, ltc7880,
+ ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132,
+ ltc7841, ltc7880,
/* Modules */
ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686,
ltm4700,
@@ -50,7 +51,7 @@ enum chips {
#define LTC3880_MFR_CLEAR_PEAKS 0xe3
#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4
-/* LTC3883, LTC3884, LTC3886, LTC3889, LTC7132, LTC7880 */
+/* LTC3883, LTC3884, LTC3886, LTC3889, LTC7132, LTC7841 and LTC7880 only */
#define LTC3883_MFR_IIN_PEAK 0xe1
/* LTC2975 only */
@@ -80,6 +81,7 @@ enum chips {
#define LTC3887_ID 0x4700
#define LTC3889_ID 0x4900
#define LTC7132_ID 0x4CE0
+#define LTC7841_ID 0x40D0
#define LTC7880_ID 0x49E0
#define LTM2987_ID_A 0x8010 /* A/B for two die IDs */
#define LTM2987_ID_B 0x8020
@@ -548,6 +550,7 @@ static const struct i2c_device_id ltc2978_id[] = {
{"ltc3887", ltc3887},
{"ltc3889", ltc3889},
{"ltc7132", ltc7132},
+ {"ltc7841", ltc7841},
{"ltc7880", ltc7880},
{"ltm2987", ltm2987},
{"ltm4664", ltm4664},
@@ -654,6 +657,8 @@ static int ltc2978_get_id(struct i2c_client *client)
return ltc3889;
else if (chip_id == LTC7132_ID)
return ltc7132;
+ else if (chip_id == LTC7841_ID)
+ return ltc7841;
else if (chip_id == LTC7880_ID)
return ltc7880;
else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B)
@@ -854,6 +859,16 @@ static int ltc2978_probe(struct i2c_client *client)
| PMBUS_HAVE_POUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
break;
+ case ltc7841:
+ data->features |= FEAT_CLEAR_PEAKS;
+ info->read_word_data = ltc3883_read_word_data;
+ info->pages = LTC3883_NUM_PAGES;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
+ | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ break;
default:
return -ENODEV;
}
@@ -907,6 +922,7 @@ static const struct of_device_id ltc2978_of_match[] = {
{ .compatible = "lltc,ltc3887" },
{ .compatible = "lltc,ltc3889" },
{ .compatible = "lltc,ltc7132" },
+ { .compatible = "lltc,ltc7841" },
{ .compatible = "lltc,ltc7880" },
{ .compatible = "lltc,ltm2987" },
{ .compatible = "lltc,ltm4664" },
@@ -936,4 +952,4 @@ module_i2c_driver(ltc2978_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for LTC2978 and compatible chips");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c
index f2023b17aa8d..824c16a75e2c 100644
--- a/drivers/hwmon/pmbus/ltc3815.c
+++ b/drivers/hwmon/pmbus/ltc3815.c
@@ -143,7 +143,7 @@ static int ltc3815_write_word_data(struct i2c_client *client, int page,
}
static const struct i2c_device_id ltc3815_id[] = {
- {"ltc3815", 0},
+ {"ltc3815"},
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc3815_id);
@@ -208,4 +208,4 @@ module_i2c_driver(ltc3815_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for LTC3815");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/ltc4286.c b/drivers/hwmon/pmbus/ltc4286.c
index 9e7ceeb7e789..aabd0bcdfeee 100644
--- a/drivers/hwmon/pmbus/ltc4286.c
+++ b/drivers/hwmon/pmbus/ltc4286.c
@@ -58,8 +58,8 @@ static struct pmbus_driver_info ltc4286_info = {
};
static const struct i2c_device_id ltc4286_id[] = {
- { "ltc4286", 0 },
- { "ltc4287", 1 },
+ { "ltc4286", },
+ { "ltc4287", },
{}
};
MODULE_DEVICE_TABLE(i2c, ltc4286_id);
diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c
index 2cfaa62aedd6..d5810b88ea8d 100644
--- a/drivers/hwmon/pmbus/max15301.c
+++ b/drivers/hwmon/pmbus/max15301.c
@@ -23,16 +23,15 @@
#include "pmbus.h"
static const struct i2c_device_id max15301_id[] = {
- {"bmr461", 0},
- {"max15301", 0},
+ { "bmr461" },
+ { "max15301" },
+ { "max15303" },
{}
};
MODULE_DEVICE_TABLE(i2c, max15301_id);
struct max15301_data {
int id;
- ktime_t access; /* Chip access time */
- int delay; /* Delay between chip accesses in us */
struct pmbus_driver_info info;
};
@@ -55,89 +54,6 @@ static struct max15301_data max15301_data = {
}
};
-/* This chip needs a delay between accesses */
-static inline void max15301_wait(const struct max15301_data *data)
-{
- if (data->delay) {
- s64 delta = ktime_us_delta(ktime_get(), data->access);
-
- if (delta < data->delay)
- udelay(data->delay - delta);
- }
-}
-
-static int max15301_read_word_data(struct i2c_client *client, int page,
- int phase, int reg)
-{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct max15301_data *data = to_max15301_data(info);
- int ret;
-
- if (page > 0)
- return -ENXIO;
-
- if (reg >= PMBUS_VIRT_BASE)
- return -ENXIO;
-
- max15301_wait(data);
- ret = pmbus_read_word_data(client, page, phase, reg);
- data->access = ktime_get();
-
- return ret;
-}
-
-static int max15301_read_byte_data(struct i2c_client *client, int page, int reg)
-{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct max15301_data *data = to_max15301_data(info);
- int ret;
-
- if (page > 0)
- return -ENXIO;
-
- max15301_wait(data);
- ret = pmbus_read_byte_data(client, page, reg);
- data->access = ktime_get();
-
- return ret;
-}
-
-static int max15301_write_word_data(struct i2c_client *client, int page, int reg,
- u16 word)
-{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct max15301_data *data = to_max15301_data(info);
- int ret;
-
- if (page > 0)
- return -ENXIO;
-
- if (reg >= PMBUS_VIRT_BASE)
- return -ENXIO;
-
- max15301_wait(data);
- ret = pmbus_write_word_data(client, page, reg, word);
- data->access = ktime_get();
-
- return ret;
-}
-
-static int max15301_write_byte(struct i2c_client *client, int page, u8 value)
-{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct max15301_data *data = to_max15301_data(info);
- int ret;
-
- if (page > 0)
- return -ENXIO;
-
- max15301_wait(data);
- ret = pmbus_write_byte(client, page, value);
- data->access = ktime_get();
-
- return ret;
-}
-
static int max15301_probe(struct i2c_client *client)
{
int status;
@@ -164,12 +80,7 @@ static int max15301_probe(struct i2c_client *client)
return -ENODEV;
}
- max15301_data.delay = delay;
-
- info->read_byte_data = max15301_read_byte_data;
- info->read_word_data = max15301_read_word_data;
- info->write_byte = max15301_write_byte;
- info->write_word_data = max15301_write_word_data;
+ info->access_delay = delay;
return pmbus_do_probe(client, info);
}
@@ -187,4 +98,4 @@ module_i2c_driver(max15301_driver);
MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
MODULE_DESCRIPTION("PMBus driver for Maxim MAX15301");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c
index a573a0ab9e48..eb84915c2a83 100644
--- a/drivers/hwmon/pmbus/max16064.c
+++ b/drivers/hwmon/pmbus/max16064.c
@@ -91,7 +91,7 @@ static int max16064_probe(struct i2c_client *client)
}
static const struct i2c_device_id max16064_id[] = {
- {"max16064", 0},
+ {"max16064"},
{}
};
@@ -111,4 +111,4 @@ module_i2c_driver(max16064_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c
index 3ab219504600..d696e506aafb 100644
--- a/drivers/hwmon/pmbus/max16601.c
+++ b/drivers/hwmon/pmbus/max16601.c
@@ -366,4 +366,4 @@ module_i2c_driver(max16601_driver);
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
MODULE_DESCRIPTION("PMBus driver for Maxim MAX16601");
MODULE_LICENSE("GPL v2");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c
index d56ec24764fd..95869d198ecf 100644
--- a/drivers/hwmon/pmbus/max20730.c
+++ b/drivers/hwmon/pmbus/max20730.c
@@ -787,4 +787,4 @@ module_i2c_driver(max20730_driver);
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
MODULE_DESCRIPTION("PMBus driver for Maxim MAX20710 / MAX20730 / MAX20734 / MAX20743");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c
index 6ebd71cd081b..ac8c43122133 100644
--- a/drivers/hwmon/pmbus/max20751.c
+++ b/drivers/hwmon/pmbus/max20751.c
@@ -32,7 +32,7 @@ static int max20751_probe(struct i2c_client *client)
}
static const struct i2c_device_id max20751_id[] = {
- {"max20751", 0},
+ {"max20751"},
{}
};
@@ -51,4 +51,4 @@ module_i2c_driver(max20751_driver);
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
MODULE_DESCRIPTION("PMBus driver for Maxim MAX20751");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
index 5d13bbfc8f47..1f94d38a1637 100644
--- a/drivers/hwmon/pmbus/max31785.c
+++ b/drivers/hwmon/pmbus/max31785.c
@@ -518,9 +518,9 @@ static int max31785_probe(struct i2c_client *client)
}
static const struct i2c_device_id max31785_id[] = {
- { "max31785", 0 },
- { "max31785a", 0 },
- { "max31785b", 0 },
+ { "max31785" },
+ { "max31785a" },
+ { "max31785b" },
{ },
};
@@ -549,4 +549,4 @@ module_i2c_driver(max31785_driver);
MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c
index fe7f6b1b0985..c9dda33831ff 100644
--- a/drivers/hwmon/pmbus/max34440.c
+++ b/drivers/hwmon/pmbus/max34440.c
@@ -529,4 +529,4 @@ module_i2c_driver(max34440_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c
index ae8573fdf5ba..b3a2a7492bbf 100644
--- a/drivers/hwmon/pmbus/max8688.c
+++ b/drivers/hwmon/pmbus/max8688.c
@@ -171,7 +171,7 @@ static int max8688_probe(struct i2c_client *client)
}
static const struct i2c_device_id max8688_id[] = {
- {"max8688", 0},
+ {"max8688"},
{ }
};
@@ -191,4 +191,4 @@ module_i2c_driver(max8688_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mp2856.c b/drivers/hwmon/pmbus/mp2856.c
index 6969350f5d7d..e83c70a3583f 100644
--- a/drivers/hwmon/pmbus/mp2856.c
+++ b/drivers/hwmon/pmbus/mp2856.c
@@ -46,7 +46,7 @@
#define MP2856_PAGE_NUM 2
-enum chips { mp2856 = 1, mp2857 };
+enum chips { mp2856, mp2857 };
static const int mp2856_max_phases[][MP2856_PAGE_NUM] = {
[mp2856] = { MP2856_MAX_PHASE_RAIL1, MP2856_MAX_PHASE_RAIL2 },
@@ -66,7 +66,6 @@ struct mp2856_data {
int vout_format[MP2856_PAGE_NUM];
int curr_sense_gain[MP2856_PAGE_NUM];
int max_phases[MP2856_PAGE_NUM];
- enum chips chip_id;
};
#define to_mp2856_data(x) container_of(x, struct mp2856_data, info)
@@ -397,6 +396,7 @@ static int mp2856_probe(struct i2c_client *client)
{
struct pmbus_driver_info *info;
struct mp2856_data *data;
+ enum chips chip_id;
int ret;
data = devm_kzalloc(&client->dev, sizeof(struct mp2856_data),
@@ -404,9 +404,9 @@ static int mp2856_probe(struct i2c_client *client)
if (!data)
return -ENOMEM;
- data->chip_id = (enum chips)(uintptr_t)i2c_get_match_data(client);
+ chip_id = (kernel_ulong_t)i2c_get_match_data(client);
- memcpy(data->max_phases, mp2856_max_phases[data->chip_id],
+ memcpy(data->max_phases, mp2856_max_phases[chip_id],
sizeof(data->max_phases));
memcpy(&data->info, &mp2856_info, sizeof(*info));
@@ -463,4 +463,4 @@ module_i2c_driver(mp2856_driver);
MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>");
MODULE_DESCRIPTION("PMBus driver for MPS MP2856/MP2857 device");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mp2888.c b/drivers/hwmon/pmbus/mp2888.c
index 50662ed8e3d5..772a623ca7d0 100644
--- a/drivers/hwmon/pmbus/mp2888.c
+++ b/drivers/hwmon/pmbus/mp2888.c
@@ -378,7 +378,7 @@ static int mp2888_probe(struct i2c_client *client)
}
static const struct i2c_device_id mp2888_id[] = {
- {"mp2888", 0},
+ {"mp2888"},
{}
};
@@ -404,4 +404,4 @@ module_i2c_driver(mp2888_driver);
MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>");
MODULE_DESCRIPTION("PMBus driver for MPS MP2888 device");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mp2891.c b/drivers/hwmon/pmbus/mp2891.c
new file mode 100644
index 000000000000..f8f4c91ec23c
--- /dev/null
+++ b/drivers/hwmon/pmbus/mp2891.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2891)
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include "pmbus.h"
+
+/*
+ * Vender specific registers, the register MFR_SVI3_IOUT_PRT(0x65),
+ * MFR_VOUT_LOOP_CTRL(0xBD), READ_PIN_EST(0x94)and READ_IIN_EST(0x95)
+ * redefine the standard PMBUS register. The MFR_SVI3_IOUT_PRT(0x65)
+ * is used to identify the iout scale and the MFR_VOUT_LOOP_CTRL(0xBD)
+ * is used to identify the vout scale. The READ_PIN_EST(0x94) is used
+ * to read input power per rail. The MP2891 does not have standard
+ * READ_IIN register(0x89), the iin telemetry can be obtained through
+ * the vendor redefined register READ_IIN_EST(0x95).
+ */
+#define MFR_VOUT_LOOP_CTRL 0xBD
+#define READ_PIN_EST 0x94
+#define READ_IIN_EST 0x95
+#define MFR_SVI3_IOUT_PRT 0x65
+
+#define MP2891_TEMP_LIMIT_OFFSET 40
+#define MP2891_PIN_LIMIT_UINT 2
+#define MP2891_IOUT_LIMIT_UINT 8
+#define MP2891_IOUT_SCALE_DIV 32
+#define MP2891_VOUT_SCALE_DIV 100
+#define MP2891_OVUV_DELTA_SCALE 50
+#define MP2891_OV_LIMIT_SCALE 20
+#define MP2891_UV_LIMIT_SCALE 5
+
+#define MP2891_PAGE_NUM 2
+
+#define MP2891_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | \
+ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | \
+ PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_STATUS_INPUT | \
+ PMBUS_HAVE_STATUS_TEMP)
+
+#define MP2891_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_POUT | \
+ PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | \
+ PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_STATUS_INPUT | \
+ PMBUS_HAVE_STATUS_TEMP)
+
+struct mp2891_data {
+ struct pmbus_driver_info info;
+ int vout_scale[MP2891_PAGE_NUM];
+ int iout_scale[MP2891_PAGE_NUM];
+};
+
+#define to_mp2891_data(x) container_of(x, struct mp2891_data, info)
+
+/* Converts a LINEAR11 value to DIRECT format */
+static u16 mp2891_reg2data_linear11(u16 word)
+{
+ s16 exponent;
+ s32 mantissa;
+ s64 val;
+
+ exponent = ((s16)word) >> 11;
+ mantissa = ((s16)((word & 0x7ff) << 5)) >> 5;
+ val = mantissa;
+
+ if (exponent >= 0)
+ val <<= exponent;
+ else
+ val >>= -exponent;
+
+ return val;
+}
+
+static int
+mp2891_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info,
+ int page)
+{
+ struct mp2891_data *data = to_mp2891_data(info);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MFR_VOUT_LOOP_CTRL);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The output voltage is equal to the READ_VOUT(0x8B) register value multiplied
+ * by vout_scale.
+ * Obtain vout scale from the register MFR_VOUT_LOOP_CTRL, bits 15-14,bit 13.
+ * If MFR_VOUT_LOOP_CTRL[13] = 1, the vout scale is below:
+ * 2.5mV/LSB
+ * If MFR_VOUT_LOOP_CTRL[13] = 0, the vout scale is decided by
+ * MFR_VOUT_LOOP_CTRL[15:14]:
+ * 00b - 6.25mV/LSB, 01b - 5mV/LSB, 10b - 2mV/LSB, 11b - 1mV
+ */
+ if (ret & GENMASK(13, 13)) {
+ data->vout_scale[page] = 250;
+ } else {
+ ret = FIELD_GET(GENMASK(15, 14), ret);
+ if (ret == 0)
+ data->vout_scale[page] = 625;
+ else if (ret == 1)
+ data->vout_scale[page] = 500;
+ else if (ret == 2)
+ data->vout_scale[page] = 200;
+ else
+ data->vout_scale[page] = 100;
+ }
+
+ return 0;
+}
+
+static int
+mp2891_identify_iout_scale(struct i2c_client *client, struct pmbus_driver_info *info,
+ int page)
+{
+ struct mp2891_data *data = to_mp2891_data(info);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MFR_SVI3_IOUT_PRT);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The output current is equal to the READ_IOUT(0x8C) register value
+ * multiplied by iout_scale.
+ * Obtain iout_scale from the register MFR_SVI3_IOUT_PRT[2:0].
+ * The value is selected as below:
+ * 000b - 1A/LSB, 001b - (1/32)A/LSB, 010b - (1/16)A/LSB,
+ * 011b - (1/8)A/LSB, 100b - (1/4)A/LSB, 101b - (1/2)A/LSB
+ * 110b - 1A/LSB, 111b - 2A/LSB
+ */
+ switch (ret & GENMASK(2, 0)) {
+ case 0:
+ case 6:
+ data->iout_scale[page] = 32;
+ break;
+ case 1:
+ data->iout_scale[page] = 1;
+ break;
+ case 2:
+ data->iout_scale[page] = 2;
+ break;
+ case 3:
+ data->iout_scale[page] = 4;
+ break;
+ case 4:
+ data->iout_scale[page] = 8;
+ break;
+ case 5:
+ data->iout_scale[page] = 16;
+ break;
+ default:
+ data->iout_scale[page] = 64;
+ break;
+ }
+
+ return 0;
+}
+
+static int mp2891_identify(struct i2c_client *client, struct pmbus_driver_info *info)
+{
+ int ret;
+
+ /* Identify vout scale for rail 1. */
+ ret = mp2891_identify_vout_scale(client, info, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Identify vout scale for rail 2. */
+ ret = mp2891_identify_vout_scale(client, info, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Identify iout scale for rail 1. */
+ ret = mp2891_identify_iout_scale(client, info, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Identify iout scale for rail 2. */
+ return mp2891_identify_iout_scale(client, info, 1);
+}
+
+static int mp2891_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VOUT_MODE:
+ /*
+ * The MP2891 does not follow standard PMBus protocol completely, the
+ * PMBUS_VOUT_MODE(0x20) in MP2891 is reserved and 0x00 is always
+ * returned when the register is read. But the calculation of vout in
+ * this driver is based on direct format. As a result, the format of
+ * vout is enforced to direct.
+ */
+ ret = PB_VOUT_MODE_DIRECT;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+
+ return ret;
+}
+
+static int mp2891_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct mp2891_data *data = to_mp2891_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_READ_VIN:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = ret & GENMASK(9, 0);
+ break;
+ case PMBUS_READ_IIN:
+ /*
+ * The MP2891 does not have standard PMBUS_READ_IIN register(0x89),
+ * the iin telemetry can be obtained through the vender redefined
+ * register READ_IIN_EST(0x95). The MP2891 PMBUS_READ_IIN register
+ * is linear11 format, But the pout scale is set to 1A/Lsb(using
+ * r/m/b scale). As a result, the iin read from MP2891 should be
+ * calculated to A, then return the result to pmbus core.
+ */
+ ret = pmbus_read_word_data(client, page, phase, READ_IIN_EST);
+ if (ret < 0)
+ return ret;
+
+ ret = mp2891_reg2data_linear11(ret);
+ break;
+ case PMBUS_READ_PIN:
+ /*
+ * The MP2891 has standard PMBUS_READ_PIN register(0x97), but this
+ * is not used to read the input power per rail. The input power
+ * per rail is read through the vender redefined register
+ * READ_PIN_EST(0x94). The MP2891 PMBUS_READ_PIN register is linear11
+ * format, But the pout scale is set to 1W/Lsb(using r/m/b scale).
+ * As a result, the pin read from MP2891 should be calculated to W,
+ * then return the result to pmbus core.
+ */
+ ret = pmbus_read_word_data(client, page, phase, READ_PIN_EST);
+ if (ret < 0)
+ return ret;
+
+ ret = mp2891_reg2data_linear11(ret);
+ break;
+ case PMBUS_READ_POUT:
+ /*
+ * The MP2891 PMBUS_READ_POUT register is linear11 format, and the
+ * exponent is not a constant value. But the pout scale is set to
+ * 1W/Lsb(using r/m/b scale). As a result, the pout read from MP2891
+ * should be calculated to W, then return the result to pmbus core.
+ */
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = mp2891_reg2data_linear11(ret);
+ break;
+ case PMBUS_READ_VOUT:
+ case PMBUS_VOUT_UV_WARN_LIMIT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST(ret * data->vout_scale[page], MP2891_VOUT_SCALE_DIV);
+ break;
+ case PMBUS_READ_IOUT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(10, 0)) * data->iout_scale[page],
+ MP2891_IOUT_SCALE_DIV);
+ break;
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ /*
+ * The scale of MP2891 PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT
+ * is 1°C/LSB and they have 40°C offset.
+ */
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = (ret & GENMASK(7, 0)) - MP2891_TEMP_LIMIT_OFFSET;
+ break;
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ /*
+ * The MP2891 PMBUS_VIN_OV_FAULT_LIMIT scale is 125mV/Lsb.
+ * but the vin scale is set to 31.25mV/Lsb(using r/m/b scale).
+ * As a result, the limit value should be multiplied by 4.
+ */
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = (ret & GENMASK(7, 0)) * 4;
+ break;
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ if (FIELD_GET(GENMASK(11, 8), ret))
+ ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_UV_LIMIT_SCALE -
+ (FIELD_GET(GENMASK(11, 8), ret) + 1) * MP2891_OVUV_DELTA_SCALE;
+ else
+ ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_UV_LIMIT_SCALE;
+
+ ret = ret < 0 ? 0 : ret;
+ break;
+ case PMBUS_VOUT_OV_FAULT_LIMIT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ if (FIELD_GET(GENMASK(11, 8), ret))
+ ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_OV_LIMIT_SCALE +
+ (FIELD_GET(GENMASK(11, 8), ret) + 1) * MP2891_OVUV_DELTA_SCALE;
+ else
+ ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_OV_LIMIT_SCALE;
+ break;
+ case PMBUS_IOUT_OC_WARN_LIMIT:
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * data->iout_scale[page] *
+ MP2891_IOUT_LIMIT_UINT, MP2891_IOUT_SCALE_DIV);
+ break;
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ /*
+ * The scale of PMBUS_IIN_OC_WARN_LIMIT is 0.5A/Lsb, but the iin scale
+ * is set to 1A/Lsb(using r/m/b scale), so the word data should be
+ * divided by 2.
+ */
+ ret = pmbus_read_word_data(client, 0, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)), 2);
+ break;
+ case PMBUS_PIN_OP_WARN_LIMIT:
+ /*
+ * The scale of PMBUS_PIN_OP_WARN_LIMIT is 2W/Lsb, but the pin scale
+ * is set to 1W/Lsb(using r/m/b scale), so the word data should be
+ * multiplied by 2.
+ */
+ ret = pmbus_read_word_data(client, 0, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = (ret & GENMASK(9, 0)) * MP2891_PIN_LIMIT_UINT;
+ break;
+ case PMBUS_READ_TEMPERATURE_1:
+ case PMBUS_VIN_UV_FAULT_LIMIT:
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ ret = -ENODATA;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int mp2891_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct mp2891_data *data = to_mp2891_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VOUT_UV_WARN_LIMIT:
+ ret = pmbus_write_word_data(client, page, reg,
+ DIV_ROUND_CLOSEST(word * MP2891_VOUT_SCALE_DIV,
+ data->vout_scale[page]));
+ break;
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ /*
+ * The PMBUS_VOUT_UV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15
+ * should not be changed.
+ */
+ ret = pmbus_read_word_data(client, page, 0xff, reg);
+ if (ret < 0)
+ return ret;
+
+ if (FIELD_GET(GENMASK(11, 8), ret))
+ ret = pmbus_write_word_data(client, page, reg,
+ (ret & ~GENMASK(7, 0)) |
+ FIELD_PREP(GENMASK(7, 0),
+ DIV_ROUND_CLOSEST(word +
+ (FIELD_GET(GENMASK(11, 8), ret) + 1) *
+ MP2891_OVUV_DELTA_SCALE,
+ MP2891_UV_LIMIT_SCALE)));
+ else
+ ret = pmbus_write_word_data(client, page, reg,
+ (ret & ~GENMASK(7, 0)) |
+ FIELD_PREP(GENMASK(7, 0),
+ DIV_ROUND_CLOSEST(word,
+ MP2891_UV_LIMIT_SCALE)));
+ break;
+ case PMBUS_VOUT_OV_FAULT_LIMIT:
+ /*
+ * The PMBUS_VOUT_OV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15
+ * should not be changed.
+ */
+ ret = pmbus_read_word_data(client, page, 0xff, reg);
+ if (ret < 0)
+ return ret;
+
+ if (FIELD_GET(GENMASK(11, 8), ret))
+ ret = pmbus_write_word_data(client, page, reg,
+ (ret & ~GENMASK(7, 0)) |
+ FIELD_PREP(GENMASK(7, 0),
+ DIV_ROUND_CLOSEST(word -
+ (FIELD_GET(GENMASK(11, 8), ret) + 1) *
+ MP2891_OVUV_DELTA_SCALE,
+ MP2891_OV_LIMIT_SCALE)));
+ else
+ ret = pmbus_write_word_data(client, page, reg,
+ (ret & ~GENMASK(7, 0)) |
+ FIELD_PREP(GENMASK(7, 0),
+ DIV_ROUND_CLOSEST(word,
+ MP2891_OV_LIMIT_SCALE)));
+ break;
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ /*
+ * The PMBUS_VIN_OV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15
+ * should not be changed. The scale of PMBUS_VIN_OV_FAULT_LIMIT is 125mV/Lsb,
+ * but the vin scale is set to 31.25mV/Lsb(using r/m/b scale), so the word data
+ * should be divided by 4.
+ */
+ ret = pmbus_read_word_data(client, page, 0xff, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = pmbus_write_word_data(client, page, reg,
+ (ret & ~GENMASK(7, 0)) |
+ FIELD_PREP(GENMASK(7, 0),
+ DIV_ROUND_CLOSEST(word, 4)));
+ break;
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ /*
+ * The scale of MP2891 PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT
+ * have 40°C offset. The bit0-bit7 is the limit value, and bit8-bit15
+ * should not be changed.
+ */
+ ret = pmbus_read_word_data(client, page, 0xff, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = pmbus_write_word_data(client, page, reg,
+ (ret & ~GENMASK(7, 0)) |
+ FIELD_PREP(GENMASK(7, 0), word + MP2891_TEMP_LIMIT_OFFSET));
+ break;
+ case PMBUS_IOUT_OC_WARN_LIMIT:
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ ret = pmbus_write_word_data(client, page, reg,
+ DIV_ROUND_CLOSEST(word * MP2891_IOUT_SCALE_DIV,
+ MP2891_IOUT_LIMIT_UINT *
+ data->iout_scale[page]));
+ break;
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ /*
+ * The scale of PMBUS_IIN_OC_WARN_LIMIT is 0.5A/Lsb, but the iin scale
+ * is set to 1A/Lsb(using r/m/b scale), so the word data should be
+ * multiplied by 2.
+ */
+ ret = pmbus_write_word_data(client, page, reg, word * 2);
+ break;
+ case PMBUS_PIN_OP_WARN_LIMIT:
+ /*
+ * The scale of PMBUS_PIN_OP_WARN_LIMIT is 2W/Lsb, but the pin scale
+ * is set to 1W/Lsb(using r/m/b scale), so the word data should be
+ * divided by 2.
+ */
+ ret = pmbus_write_word_data(client, page, reg,
+ DIV_ROUND_CLOSEST(word, MP2891_PIN_LIMIT_UINT));
+ break;
+ case PMBUS_VIN_UV_FAULT_LIMIT:
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ ret = -ENODATA;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct pmbus_driver_info mp2891_info = {
+ .pages = MP2891_PAGE_NUM,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_CURRENT_IN] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_POWER] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+
+ /* set vin scale 31.25mV/Lsb */
+ .m[PSC_VOLTAGE_IN] = 32,
+ .R[PSC_VOLTAGE_IN] = 0,
+ .b[PSC_VOLTAGE_IN] = 0,
+
+ /* set temp scale 1000m°C/Lsb */
+ .m[PSC_TEMPERATURE] = 1,
+ .R[PSC_TEMPERATURE] = 0,
+ .b[PSC_TEMPERATURE] = 0,
+
+ .m[PSC_CURRENT_IN] = 1,
+ .R[PSC_CURRENT_IN] = 0,
+ .b[PSC_CURRENT_IN] = 0,
+
+ .m[PSC_CURRENT_OUT] = 1,
+ .R[PSC_CURRENT_OUT] = 0,
+ .b[PSC_CURRENT_OUT] = 0,
+
+ .m[PSC_POWER] = 1,
+ .R[PSC_POWER] = 0,
+ .b[PSC_POWER] = 0,
+
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .b[PSC_VOLTAGE_OUT] = 0,
+
+ .func[0] = MP2891_RAIL1_FUNC,
+ .func[1] = MP2891_RAIL2_FUNC,
+ .read_word_data = mp2891_read_word_data,
+ .write_word_data = mp2891_write_word_data,
+ .read_byte_data = mp2891_read_byte_data,
+ .identify = mp2891_identify,
+};
+
+static int mp2891_probe(struct i2c_client *client)
+{
+ struct mp2891_data *data;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ memcpy(&data->info, &mp2891_info, sizeof(mp2891_info));
+
+ return pmbus_do_probe(client, &data->info);
+}
+
+static const struct i2c_device_id mp2891_id[] = {
+ { "mp2891" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mp2891_id);
+
+static const struct of_device_id __maybe_unused mp2891_of_match[] = {
+ {.compatible = "mps,mp2891"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mp2891_of_match);
+
+static struct i2c_driver mp2891_driver = {
+ .driver = {
+ .name = "mp2891",
+ .of_match_table = mp2891_of_match,
+ },
+ .probe = mp2891_probe,
+ .id_table = mp2891_id,
+};
+
+module_i2c_driver(mp2891_driver);
+
+MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>");
+MODULE_DESCRIPTION("PMBus driver for MPS MP2891");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c
index e5fa10b3b8bc..c31982d85196 100644
--- a/drivers/hwmon/pmbus/mp2975.c
+++ b/drivers/hwmon/pmbus/mp2975.c
@@ -5,12 +5,14 @@
* Copyright (C) 2020 Nvidia Technologies Ltd.
*/
+#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+
#include "pmbus.h"
/* Vendor specific registers. */
@@ -97,6 +99,11 @@ static const int mp2975_max_phases[][MP2975_PAGE_NUM] = {
[mp2971] = { MP2971_MAX_PHASE_RAIL1, MP2971_MAX_PHASE_RAIL2 },
};
+struct mp2975_driver_info {
+ const struct pmbus_driver_info *info;
+ enum chips chip_id;
+};
+
struct mp2975_data {
struct pmbus_driver_info info;
enum chips chip_id;
@@ -110,15 +117,6 @@ struct mp2975_data {
int curr_sense_gain[MP2975_PAGE_NUM];
};
-static const struct i2c_device_id mp2975_id[] = {
- {"mp2971", mp2971},
- {"mp2973", mp2973},
- {"mp2975", mp2975},
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, mp2975_id);
-
static const struct regulator_desc __maybe_unused mp2975_reg_desc[] = {
PMBUS_REGULATOR("vout", 0),
PMBUS_REGULATOR("vout", 1),
@@ -392,6 +390,80 @@ static int mp2973_read_word_data(struct i2c_client *client, int page,
return ret;
}
+static int mp2973_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ u8 target, mask;
+ long ret;
+
+ if (reg != PMBUS_SMBALERT_MASK)
+ return -ENODATA;
+
+ /*
+ * Vendor-specific SMBALERT_MASK register with 16 maskable bits.
+ */
+ ret = pmbus_read_word_data(client, 0, 0, PMBUS_SMBALERT_MASK);
+ if (ret < 0)
+ return ret;
+
+ target = word & 0xff;
+ mask = word >> 8;
+
+/*
+ * Set/Clear 'bit' in 'ret' based on condition followed by define for each bit in SMBALERT_MASK.
+ * Also bit 2 & 15 are reserved.
+ */
+
+#define MP2973_TEMP_OT 0
+#define MP2973_VIN_UVLO 1
+#define MP2973_VIN_OVP 3
+#define MP2973_MTP_FAULT 4
+#define MP2973_OTHER_COMM 5
+#define MP2973_MTP_BLK_TRIG 6
+#define MP2973_PACKET_ERROR 7
+#define MP2973_INVALID_DATA 8
+#define MP2973_INVALID_COMMAND 9
+#define MP2973_IOUT_OC_LV 10
+#define MP2973_IOUT_OC 11
+#define MP2973_VOUT_MAX_MIN_WARNING 12
+#define MP2973_VOLTAGE_UV 13
+#define MP2973_VOLTAGE_OV 14
+
+ switch (target) {
+ case PMBUS_STATUS_CML:
+ __assign_bit(MP2973_INVALID_DATA, &ret, !(mask & PB_CML_FAULT_INVALID_DATA));
+ __assign_bit(MP2973_INVALID_COMMAND, &ret, !(mask & PB_CML_FAULT_INVALID_COMMAND));
+ __assign_bit(MP2973_OTHER_COMM, &ret, !(mask & PB_CML_FAULT_OTHER_COMM));
+ __assign_bit(MP2973_PACKET_ERROR, &ret, !(mask & PB_CML_FAULT_PACKET_ERROR));
+ break;
+ case PMBUS_STATUS_VOUT:
+ __assign_bit(MP2973_VOLTAGE_UV, &ret, !(mask & PB_VOLTAGE_UV_FAULT));
+ __assign_bit(MP2973_VOLTAGE_OV, &ret, !(mask & PB_VOLTAGE_OV_FAULT));
+ break;
+ case PMBUS_STATUS_IOUT:
+ __assign_bit(MP2973_IOUT_OC, &ret, !(mask & PB_IOUT_OC_FAULT));
+ __assign_bit(MP2973_IOUT_OC_LV, &ret, !(mask & PB_IOUT_OC_LV_FAULT));
+ break;
+ case PMBUS_STATUS_TEMPERATURE:
+ __assign_bit(MP2973_TEMP_OT, &ret, !(mask & PB_TEMP_OT_FAULT));
+ break;
+ /*
+ * Map remaining bits to MFR specific to let the PMBUS core mask
+ * those bits by default.
+ */
+ case PMBUS_STATUS_MFR_SPECIFIC:
+ __assign_bit(MP2973_VIN_UVLO, &ret, !(mask & BIT(1)));
+ __assign_bit(MP2973_VIN_OVP, &ret, !(mask & BIT(3)));
+ __assign_bit(MP2973_MTP_FAULT, &ret, !(mask & BIT(4)));
+ __assign_bit(MP2973_MTP_BLK_TRIG, &ret, !(mask & BIT(6)));
+ break;
+ default:
+ return 0;
+ }
+
+ return pmbus_write_word_data(client, 0, PMBUS_SMBALERT_MASK, ret);
+}
+
static int mp2975_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
@@ -867,7 +939,7 @@ mp2975_vout_per_rail_config_get(struct i2c_client *client,
return 0;
}
-static struct pmbus_driver_info mp2975_info = {
+static const struct pmbus_driver_info mp2975_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = linear,
.format[PSC_VOLTAGE_OUT] = direct,
@@ -892,7 +964,7 @@ static struct pmbus_driver_info mp2975_info = {
#endif
};
-static struct pmbus_driver_info mp2973_info = {
+static const struct pmbus_driver_info mp2973_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = linear,
.format[PSC_VOLTAGE_OUT] = direct,
@@ -907,35 +979,41 @@ static struct pmbus_driver_info mp2973_info = {
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT |
PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,
.read_word_data = mp2973_read_word_data,
+ .write_word_data = mp2973_write_word_data,
#if IS_ENABLED(CONFIG_SENSORS_MP2975_REGULATOR)
.num_regulators = 1,
.reg_desc = mp2975_reg_desc,
#endif
};
+static const struct mp2975_driver_info mp2975_ddinfo[] = {
+ [mp2975] = { .info = &mp2975_info, .chip_id = mp2975 },
+ [mp2973] = { .info = &mp2973_info, .chip_id = mp2973 },
+ [mp2971] = { .info = &mp2973_info, .chip_id = mp2971 },
+};
+
static int mp2975_probe(struct i2c_client *client)
{
+ const struct mp2975_driver_info *ddinfo;
struct pmbus_driver_info *info;
struct mp2975_data *data;
int ret;
+ ddinfo = i2c_get_match_data(client);
+ if (!ddinfo)
+ return -ENODEV;
+
data = devm_kzalloc(&client->dev, sizeof(struct mp2975_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
- if (client->dev.of_node)
- data->chip_id = (enum chips)(unsigned long)of_device_get_match_data(&client->dev);
- else
- data->chip_id = i2c_match_id(mp2975_id, client)->driver_data;
+ data->chip_id = ddinfo->chip_id;
memcpy(data->max_phases, mp2975_max_phases[data->chip_id],
sizeof(data->max_phases));
- if (data->chip_id == mp2975)
- memcpy(&data->info, &mp2975_info, sizeof(*info));
- else
- memcpy(&data->info, &mp2973_info, sizeof(*info));
+ memcpy(&data->info, ddinfo->info, sizeof(data->info));
info = &data->info;
@@ -993,18 +1071,26 @@ static int mp2975_probe(struct i2c_client *client)
return pmbus_do_probe(client, info);
}
-static const struct of_device_id __maybe_unused mp2975_of_match[] = {
- {.compatible = "mps,mp2971", .data = (void *)mp2971},
- {.compatible = "mps,mp2973", .data = (void *)mp2973},
- {.compatible = "mps,mp2975", .data = (void *)mp2975},
+static const struct of_device_id mp2975_of_match[] = {
+ {.compatible = "mps,mp2971", .data = &mp2975_ddinfo[mp2971]},
+ {.compatible = "mps,mp2973", .data = &mp2975_ddinfo[mp2973]},
+ {.compatible = "mps,mp2975", .data = &mp2975_ddinfo[mp2975]},
{}
};
MODULE_DEVICE_TABLE(of, mp2975_of_match);
+static const struct i2c_device_id mp2975_id[] = {
+ {"mp2971", (kernel_ulong_t)&mp2975_ddinfo[mp2971]},
+ {"mp2973", (kernel_ulong_t)&mp2975_ddinfo[mp2973]},
+ {"mp2975", (kernel_ulong_t)&mp2975_ddinfo[mp2975]},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mp2975_id);
+
static struct i2c_driver mp2975_driver = {
.driver = {
.name = "mp2975",
- .of_match_table = of_match_ptr(mp2975_of_match),
+ .of_match_table = mp2975_of_match,
},
.probe = mp2975_probe,
.id_table = mp2975_id,
@@ -1015,4 +1101,4 @@ module_i2c_driver(mp2975_driver);
MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>");
MODULE_DESCRIPTION("PMBus driver for MPS MP2975 device");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mp2993.c b/drivers/hwmon/pmbus/mp2993.c
new file mode 100644
index 000000000000..81c84fc8ed47
--- /dev/null
+++ b/drivers/hwmon/pmbus/mp2993.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2993)
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include "pmbus.h"
+
+#define MP2993_VOUT_OVUV_UINT 125
+#define MP2993_VOUT_OVUV_DIV 64
+#define MP2993_VIN_LIMIT_UINT 1
+#define MP2993_VIN_LIMIT_DIV 8
+#define MP2993_READ_VIN_UINT 1
+#define MP2993_READ_VIN_DIV 32
+
+#define MP2993_PAGE_NUM 2
+
+#define MP2993_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | \
+ PMBUS_HAVE_IIN | \
+ PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_STATUS_TEMP | \
+ PMBUS_HAVE_STATUS_INPUT)
+
+#define MP2993_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \
+ PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | \
+ PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_STATUS_TEMP | \
+ PMBUS_HAVE_STATUS_INPUT)
+
+/* Converts a linear11 data exponent to a specified value */
+static u16 mp2993_linear11_exponent_transfer(u16 word, u16 expect_exponent)
+{
+ s16 exponent, mantissa, target_exponent;
+
+ exponent = ((s16)word) >> 11;
+ mantissa = ((s16)((word & 0x7ff) << 5)) >> 5;
+ target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11;
+
+ if (exponent > target_exponent)
+ mantissa = mantissa << (exponent - target_exponent);
+ else
+ mantissa = mantissa >> (target_exponent - exponent);
+
+ return (mantissa & 0x7ff) | ((expect_exponent << 11) & 0xf800);
+}
+
+static int
+mp2993_set_vout_format(struct i2c_client *client, int page, int format)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_write_byte_data(client, PMBUS_VOUT_MODE, format);
+}
+
+static int mp2993_identify(struct i2c_client *client, struct pmbus_driver_info *info)
+{
+ int ret;
+
+ /* Set vout to direct format for rail1. */
+ ret = mp2993_set_vout_format(client, 0, PB_VOUT_MODE_DIRECT);
+ if (ret < 0)
+ return ret;
+
+ /* Set vout to direct format for rail2. */
+ return mp2993_set_vout_format(client, 1, PB_VOUT_MODE_DIRECT);
+}
+
+static int mp2993_read_word_data(struct i2c_client *client, int page, int phase,
+ int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VOUT_OV_FAULT_LIMIT:
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST(ret * MP2993_VOUT_OVUV_UINT, MP2993_VOUT_OVUV_DIV);
+ break;
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ /*
+ * The MP2993 ot fault limit value and ot warn limit value
+ * per rail are always the same, so only PMBUS_OT_FAULT_LIMIT
+ * and PMBUS_OT_WARN_LIMIT register in page 0 are defined to
+ * indicates the limit value.
+ */
+ ret = pmbus_read_word_data(client, 0, phase, reg);
+ break;
+ case PMBUS_READ_VIN:
+ /* The MP2993 vin scale is (1/32V)/Lsb */
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)) * MP2993_READ_VIN_UINT,
+ MP2993_READ_VIN_DIV);
+ break;
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ case PMBUS_VIN_OV_WARN_LIMIT:
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ case PMBUS_VIN_UV_FAULT_LIMIT:
+ /* The MP2993 vin limit scale is (1/8V)/Lsb */
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * MP2993_VIN_LIMIT_UINT,
+ MP2993_VIN_LIMIT_DIV);
+ break;
+ case PMBUS_READ_IOUT:
+ case PMBUS_READ_IIN:
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ case PMBUS_IOUT_OC_WARN_LIMIT:
+ case PMBUS_READ_VOUT:
+ case PMBUS_READ_PIN:
+ case PMBUS_READ_POUT:
+ case PMBUS_READ_TEMPERATURE_1:
+ ret = -ENODATA;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int mp2993_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VOUT_OV_FAULT_LIMIT:
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ ret = DIV_ROUND_CLOSEST(word * MP2993_VOUT_OVUV_DIV, MP2993_VOUT_OVUV_UINT);
+ ret = pmbus_write_word_data(client, 0, reg, ret);
+ break;
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ /*
+ * The MP2993 ot fault limit value and ot warn limit value
+ * per rail are always the same, so only PMBUS_OT_FAULT_LIMIT
+ * and PMBUS_OT_WARN_LIMIT register in page 0 are defined to
+ * config the ot limit value.
+ */
+ ret = pmbus_write_word_data(client, 0, reg, word);
+ break;
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ case PMBUS_VIN_OV_WARN_LIMIT:
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ case PMBUS_VIN_UV_FAULT_LIMIT:
+ /* The MP2993 vin limit scale is (1/8V)/Lsb */
+ ret = pmbus_write_word_data(client, 0, reg,
+ DIV_ROUND_CLOSEST(word * MP2993_VIN_LIMIT_DIV,
+ MP2993_VIN_LIMIT_UINT));
+ break;
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ /*
+ * The PMBUS_IIN_OC_WARN_LIMIT of MP2993 is linear11 format,
+ * and the exponent is a constant value(5'b00000), so the
+ * exponent of word parameter should be converted to 5'b00000.
+ */
+ ret = pmbus_write_word_data(client, page, reg,
+ mp2993_linear11_exponent_transfer(word, 0x00));
+ break;
+ //
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ case PMBUS_IOUT_OC_WARN_LIMIT:
+ /*
+ * The PMBUS_IOUT_OC_FAULT_LIMIT and PMBUS_IOUT_OC_WARN_LIMIT
+ * of MP2993 can be regarded as linear11 format, and the
+ * exponent is a 5'b00001 or 5'b00000. To ensure a larger
+ * range of limit value, so the exponent of word parameter
+ * should be converted to 5'b00001.
+ */
+ ret = pmbus_write_word_data(client, page, reg,
+ mp2993_linear11_exponent_transfer(word, 0x01));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static struct pmbus_driver_info mp2993_info = {
+ .pages = MP2993_PAGE_NUM,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_CURRENT_IN] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_POWER] = linear,
+ .format[PSC_VOLTAGE_OUT] = direct,
+
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .b[PSC_VOLTAGE_OUT] = 0,
+
+ .m[PSC_VOLTAGE_IN] = 1,
+ .R[PSC_VOLTAGE_IN] = 0,
+ .b[PSC_VOLTAGE_IN] = 0,
+
+ .m[PSC_TEMPERATURE] = 1,
+ .R[PSC_TEMPERATURE] = 0,
+ .b[PSC_TEMPERATURE] = 0,
+
+ .func[0] = MP2993_RAIL1_FUNC,
+ .func[1] = MP2993_RAIL2_FUNC,
+ .read_word_data = mp2993_read_word_data,
+ .write_word_data = mp2993_write_word_data,
+ .identify = mp2993_identify,
+};
+
+static int mp2993_probe(struct i2c_client *client)
+{
+ return pmbus_do_probe(client, &mp2993_info);
+}
+
+static const struct i2c_device_id mp2993_id[] = {
+ { "mp2993" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mp2993_id);
+
+static const struct of_device_id __maybe_unused mp2993_of_match[] = {
+ {.compatible = "mps,mp2993"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mp2993_of_match);
+
+static struct i2c_driver mp2993_driver = {
+ .driver = {
+ .name = "mp2993",
+ .of_match_table = mp2993_of_match,
+ },
+ .probe = mp2993_probe,
+ .id_table = mp2993_id,
+};
+
+module_i2c_driver(mp2993_driver);
+
+MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>");
+MODULE_DESCRIPTION("PMBus driver for MPS MP2993");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mp5023.c b/drivers/hwmon/pmbus/mp5023.c
index 21acb7fd9a1a..c466d67e9a8f 100644
--- a/drivers/hwmon/pmbus/mp5023.c
+++ b/drivers/hwmon/pmbus/mp5023.c
@@ -64,4 +64,4 @@ module_i2c_driver(mp5023_driver);
MODULE_AUTHOR("Howard Chiu <howard.chiu@quantatw.com>");
MODULE_DESCRIPTION("PMBus driver for MPS MP5023 HSC");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mp5920.c b/drivers/hwmon/pmbus/mp5920.c
new file mode 100644
index 000000000000..319ae2721bcf
--- /dev/null
+++ b/drivers/hwmon/pmbus/mp5920.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for MP5920 and compatible chips.
+ */
+
+#include <linux/i2c.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include "pmbus.h"
+
+static struct pmbus_driver_info mp5920_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_POWER] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_VOLTAGE_IN] = 2266,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = -1,
+ .m[PSC_VOLTAGE_OUT] = 2266,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = -1,
+ .m[PSC_CURRENT_OUT] = 546,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = -2,
+ .m[PSC_POWER] = 5840,
+ .b[PSC_POWER] = 0,
+ .R[PSC_POWER] = -3,
+ .m[PSC_TEMPERATURE] = 1067,
+ .b[PSC_TEMPERATURE] = 20500,
+ .R[PSC_TEMPERATURE] = -2,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT |
+ PMBUS_HAVE_TEMP,
+};
+
+static int mp5920_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int ret;
+ u8 buf[I2C_SMBUS_BLOCK_MAX];
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read PMBUS_MFR_MODEL\n");
+
+ if (ret != 6 || strncmp(buf, "MP5920", 6)) {
+ return dev_err_probe(dev, -ENODEV, "Model '%.*s' not supported\n",
+ min_t(int, ret, sizeof(buf)), buf);
+ }
+
+ return pmbus_do_probe(client, &mp5920_info);
+}
+
+static const struct of_device_id mp5920_of_match[] = {
+ { .compatible = "mps,mp5920" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, mp5920_of_match);
+
+static const struct i2c_device_id mp5920_id[] = {
+ { "mp5920" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, mp5920_id);
+
+static struct i2c_driver mp5920_driver = {
+ .driver = {
+ .name = "mp5920",
+ .of_match_table = mp5920_of_match,
+ },
+ .probe = mp5920_probe,
+ .id_table = mp5920_id,
+};
+
+module_i2c_driver(mp5920_driver);
+
+MODULE_AUTHOR("Tony Ao <tony_ao@wiwynn.com>");
+MODULE_AUTHOR("Alex Vdovydchenko <xzeol@yahoo.com>");
+MODULE_DESCRIPTION("PMBus driver for MP5920 HSC");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mp5990.c b/drivers/hwmon/pmbus/mp5990.c
index 1dfbab25a064..4ce381a39480 100644
--- a/drivers/hwmon/pmbus/mp5990.c
+++ b/drivers/hwmon/pmbus/mp5990.c
@@ -158,7 +158,7 @@ static const struct of_device_id mp5990_of_match[] = {
};
static const struct i2c_device_id mp5990_id[] = {
- {"mp5990", 0},
+ {"mp5990"},
{ }
};
MODULE_DEVICE_TABLE(i2c, mp5990_id);
@@ -176,4 +176,4 @@ module_i2c_driver(mp5990_driver);
MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>");
MODULE_DESCRIPTION("PMBus driver for MP5990 HSC");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mp9941.c b/drivers/hwmon/pmbus/mp9941.c
new file mode 100644
index 000000000000..42ca6748777a
--- /dev/null
+++ b/drivers/hwmon/pmbus/mp9941.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP9941)
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include "pmbus.h"
+
+/*
+ * Vender specific registers. The MFR_ICC_MAX(0x02) is used to
+ * config the iin scale. The MFR_RESO_SET(0xC7) is used to
+ * config the vout format. The MFR_VR_MULTI_CONFIG_R1(0x0D) is
+ * used to identify the vout vid step.
+ */
+#define MFR_ICC_MAX 0x02
+#define MFR_RESO_SET 0xC7
+#define MFR_VR_MULTI_CONFIG_R1 0x0D
+
+#define MP9941_VIN_LIMIT_UINT 1
+#define MP9941_VIN_LIMIT_DIV 8
+#define MP9941_READ_VIN_UINT 1
+#define MP9941_READ_VIN_DIV 32
+
+#define MP9941_PAGE_NUM 1
+
+#define MP9941_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | \
+ PMBUS_HAVE_IIN | \
+ PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_STATUS_TEMP | \
+ PMBUS_HAVE_STATUS_INPUT)
+
+struct mp9941_data {
+ struct pmbus_driver_info info;
+ int vid_resolution;
+};
+
+#define to_mp9941_data(x) container_of(x, struct mp9941_data, info)
+
+static int mp9941_set_vout_format(struct i2c_client *client)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MFR_RESO_SET);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * page = 0, MFR_RESO_SET[7:6] defines the vout format
+ * 2'b11 set the vout format as direct
+ */
+ ret = (ret & ~GENMASK(7, 6)) | FIELD_PREP(GENMASK(7, 6), 3);
+
+ return i2c_smbus_write_word_data(client, MFR_RESO_SET, ret);
+}
+
+static int
+mp9941_identify_vid_resolution(struct i2c_client *client, struct pmbus_driver_info *info)
+{
+ struct mp9941_data *data = to_mp9941_data(info);
+ int ret;
+
+ /*
+ * page = 2, MFR_VR_MULTI_CONFIG_R1[4:4] defines rail1 vid step value
+ * 1'b0 represents the vid step value is 10mV
+ * 1'b1 represents the vid step value is 5mV
+ */
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MFR_VR_MULTI_CONFIG_R1);
+ if (ret < 0)
+ return ret;
+
+ if (FIELD_GET(GENMASK(4, 4), ret))
+ data->vid_resolution = 5;
+ else
+ data->vid_resolution = 10;
+
+ return 0;
+}
+
+static int mp9941_identify_iin_scale(struct i2c_client *client)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MFR_RESO_SET);
+ if (ret < 0)
+ return ret;
+
+ ret = (ret & ~GENMASK(3, 2)) | FIELD_PREP(GENMASK(3, 2), 0);
+
+ ret = i2c_smbus_write_word_data(client, MFR_RESO_SET, ret);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * page = 2, MFR_ICC_MAX[15:13] defines the iin scale
+ * 3'b000 set the iout scale as 0.5A/Lsb
+ */
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MFR_ICC_MAX);
+ if (ret < 0)
+ return ret;
+
+ ret = (ret & ~GENMASK(15, 13)) | FIELD_PREP(GENMASK(15, 13), 0);
+
+ return i2c_smbus_write_word_data(client, MFR_ICC_MAX, ret);
+}
+
+static int mp9941_identify(struct i2c_client *client, struct pmbus_driver_info *info)
+{
+ int ret;
+
+ ret = mp9941_identify_iin_scale(client);
+ if (ret < 0)
+ return ret;
+
+ ret = mp9941_identify_vid_resolution(client, info);
+ if (ret < 0)
+ return ret;
+
+ return mp9941_set_vout_format(client);
+}
+
+static int mp9941_read_word_data(struct i2c_client *client, int page, int phase,
+ int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct mp9941_data *data = to_mp9941_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_READ_VIN:
+ /* The MP9941 vin scale is (1/32V)/Lsb */
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)) * MP9941_READ_VIN_UINT,
+ MP9941_READ_VIN_DIV);
+ break;
+ case PMBUS_READ_IIN:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = ret & GENMASK(10, 0);
+ break;
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ /* The MP9941 vin ov limit scale is (1/8V)/Lsb */
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * MP9941_VIN_LIMIT_UINT,
+ MP9941_VIN_LIMIT_DIV);
+ break;
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = ret & GENMASK(7, 0);
+ break;
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ case PMBUS_MFR_VOUT_MIN:
+ case PMBUS_MFR_VOUT_MAX:
+ /*
+ * The vout scale is set to 1mV/Lsb(using r/m/b scale).
+ * But the vout uv limit and vout max/min scale is 1VID/Lsb,
+ * so the vout uv limit and vout max/min value should be
+ * multiplied by vid resolution.
+ */
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = ret * data->vid_resolution;
+ break;
+ case PMBUS_READ_IOUT:
+ case PMBUS_READ_POUT:
+ case PMBUS_READ_TEMPERATURE_1:
+ case PMBUS_READ_VOUT:
+ case PMBUS_READ_PIN:
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ ret = -ENODATA;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int mp9941_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct mp9941_data *data = to_mp9941_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ /* The MP9941 vin ov limit scale is (1/8V)/Lsb */
+ ret = pmbus_write_word_data(client, page, reg,
+ DIV_ROUND_CLOSEST(word * MP9941_VIN_LIMIT_DIV,
+ MP9941_VIN_LIMIT_UINT));
+ break;
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ case PMBUS_MFR_VOUT_MIN:
+ case PMBUS_MFR_VOUT_MAX:
+ ret = pmbus_write_word_data(client, page, reg,
+ DIV_ROUND_CLOSEST(word, data->vid_resolution));
+ break;
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ ret = -ENODATA;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct pmbus_driver_info mp9941_info = {
+ .pages = MP9941_PAGE_NUM,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_CURRENT_IN] = direct,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_POWER] = linear,
+ .format[PSC_TEMPERATURE] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+
+ .m[PSC_TEMPERATURE] = 1,
+ .R[PSC_TEMPERATURE] = 0,
+ .b[PSC_TEMPERATURE] = 0,
+
+ .m[PSC_VOLTAGE_IN] = 1,
+ .R[PSC_VOLTAGE_IN] = 0,
+ .b[PSC_VOLTAGE_IN] = 0,
+
+ .m[PSC_CURRENT_IN] = 2,
+ .R[PSC_CURRENT_IN] = 0,
+ .b[PSC_CURRENT_IN] = 0,
+
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .b[PSC_VOLTAGE_OUT] = 0,
+
+ .func[0] = MP9941_RAIL1_FUNC,
+ .read_word_data = mp9941_read_word_data,
+ .write_word_data = mp9941_write_word_data,
+ .identify = mp9941_identify,
+};
+
+static int mp9941_probe(struct i2c_client *client)
+{
+ struct mp9941_data *data;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ memcpy(&data->info, &mp9941_info, sizeof(mp9941_info));
+
+ return pmbus_do_probe(client, &data->info);
+}
+
+static const struct i2c_device_id mp9941_id[] = {
+ { "mp9941" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mp9941_id);
+
+static const struct of_device_id __maybe_unused mp9941_of_match[] = {
+ {.compatible = "mps,mp9941"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mp9941_of_match);
+
+static struct i2c_driver mp9941_driver = {
+ .driver = {
+ .name = "mp9941",
+ .of_match_table = mp9941_of_match,
+ },
+ .probe = mp9941_probe,
+ .id_table = mp9941_id,
+};
+
+module_i2c_driver(mp9941_driver);
+
+MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>");
+MODULE_DESCRIPTION("PMBus driver for MPS MP9941");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c
index 67487867c70f..c1e2d0cb2fd0 100644
--- a/drivers/hwmon/pmbus/mpq7932.c
+++ b/drivers/hwmon/pmbus/mpq7932.c
@@ -35,7 +35,7 @@ struct mpq7932_data {
};
#if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR)
-static struct regulator_desc mpq7932_regulators_desc[] = {
+static const struct regulator_desc mpq7932_regulators_desc[] = {
PMBUS_REGULATOR_STEP("buck", 0, MPQ7932_N_VOLTAGES,
MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
PMBUS_REGULATOR_STEP("buck", 1, MPQ7932_N_VOLTAGES,
@@ -164,4 +164,4 @@ module_i2c_driver(mpq7932_regulator_driver);
MODULE_AUTHOR("Saravanan Sekar <saravanan@linumiz.com>");
MODULE_DESCRIPTION("MPQ7932 PMIC regulator driver");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c
index 4e2549cc8120..331c274ca892 100644
--- a/drivers/hwmon/pmbus/mpq8785.c
+++ b/drivers/hwmon/pmbus/mpq8785.c
@@ -22,7 +22,7 @@ static int mpq8785_identify(struct i2c_client *client,
break;
case 1:
case 2:
- info->format[PSC_VOLTAGE_OUT] = direct,
+ info->format[PSC_VOLTAGE_OUT] = direct;
info->m[PSC_VOLTAGE_OUT] = 64;
info->b[PSC_VOLTAGE_OUT] = 0;
info->R[PSC_VOLTAGE_OUT] = 1;
@@ -62,7 +62,7 @@ static int mpq8785_probe(struct i2c_client *client)
};
static const struct i2c_device_id mpq8785_id[] = {
- { "mpq8785", 0 },
+ { "mpq8785" },
{ },
};
MODULE_DEVICE_TABLE(i2c, mpq8785_id);
@@ -87,4 +87,4 @@ module_i2c_driver(mpq8785_driver);
MODULE_AUTHOR("Charles Hsu <ythsu0511@gmail.com>");
MODULE_DESCRIPTION("PMBus driver for MPS MPQ8785");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/pim4328.c b/drivers/hwmon/pmbus/pim4328.c
index 31d9ae06379a..aa98284bbdd8 100644
--- a/drivers/hwmon/pmbus/pim4328.c
+++ b/drivers/hwmon/pmbus/pim4328.c
@@ -230,4 +230,4 @@ module_i2c_driver(pim4328_driver);
MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
MODULE_DESCRIPTION("PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/pli1209bc.c b/drivers/hwmon/pmbus/pli1209bc.c
index c95433790b11..569b61dc1a32 100644
--- a/drivers/hwmon/pmbus/pli1209bc.c
+++ b/drivers/hwmon/pmbus/pli1209bc.c
@@ -54,30 +54,6 @@ static int pli1209bc_read_word_data(struct i2c_client *client, int page,
}
}
-static int pli1209bc_write_byte(struct i2c_client *client, int page, u8 reg)
-{
- int ret;
-
- switch (reg) {
- case PMBUS_CLEAR_FAULTS:
- ret = pmbus_write_byte(client, page, reg);
- /*
- * PLI1209 takes 230 usec to execute the CLEAR_FAULTS command.
- * During that time it's busy and NACKs all requests on the
- * SMBUS interface. It also NACKs reads on PMBUS_STATUS_BYTE
- * making it impossible to poll the BUSY flag.
- *
- * Just wait for not BUSY unconditionally.
- */
- usleep_range(250, 300);
- break;
- default:
- ret = -ENODATA;
- break;
- }
- return ret;
-}
-
#if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR)
static const struct regulator_desc pli1209bc_reg_desc = {
.name = "vout2",
@@ -127,7 +103,7 @@ static struct pmbus_driver_info pli1209bc_info = {
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
| PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT,
.read_word_data = pli1209bc_read_word_data,
- .write_byte = pli1209bc_write_byte,
+ .write_delay = 250,
#if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR)
.num_regulators = 1,
.reg_desc = &pli1209bc_reg_desc,
@@ -141,7 +117,7 @@ static int pli1209bc_probe(struct i2c_client *client)
}
static const struct i2c_device_id pli1209bc_id[] = {
- {"pli1209bc", 0},
+ {"pli1209bc"},
{}
};
@@ -169,4 +145,4 @@ module_i2c_driver(pli1209bc_driver);
MODULE_AUTHOR("Marcello Sylvester Bauer <sylv@sylv.io>");
MODULE_DESCRIPTION("PMBus driver for Vicor PLI1209BC");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/pm6764tr.c b/drivers/hwmon/pmbus/pm6764tr.c
index 2a16504c85b7..c96c0aecb920 100644
--- a/drivers/hwmon/pmbus/pm6764tr.c
+++ b/drivers/hwmon/pmbus/pm6764tr.c
@@ -48,7 +48,7 @@ static int pm6764tr_probe(struct i2c_client *client)
}
static const struct i2c_device_id pm6764tr_id[] = {
- {"pm6764tr", 0},
+ {"pm6764tr"},
{}
};
MODULE_DEVICE_TABLE(i2c, pm6764tr_id);
@@ -73,4 +73,4 @@ module_i2c_driver(pm6764tr_driver);
MODULE_AUTHOR("Charles Hsu");
MODULE_DESCRIPTION("PMBus driver for ST PM6764TR");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
index ec40c5c59954..77cf268e7d2d 100644
--- a/drivers/hwmon/pmbus/pmbus.c
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -261,4 +261,4 @@ module_i2c_driver(pmbus_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("Generic PMBus driver");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index fb442fae7b3e..ddb19c9726d6 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -418,6 +418,12 @@ enum pmbus_sensor_classes {
enum pmbus_data_format { linear = 0, ieee754, direct, vid };
enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv };
+/* PMBus revision identifiers */
+#define PMBUS_REV_10 0x00 /* PMBus revision 1.0 */
+#define PMBUS_REV_11 0x11 /* PMBus revision 1.1 */
+#define PMBUS_REV_12 0x22 /* PMBus revision 1.2 */
+#define PMBUS_REV_13 0x33 /* PMBus revision 1.3 */
+
struct pmbus_driver_info {
int pages; /* Total number of pages */
u8 phases[PMBUS_PAGES]; /* Number of phases per page */
@@ -466,11 +472,23 @@ struct pmbus_driver_info {
/* custom attributes */
const struct attribute_group **groups;
+
+ /*
+ * Some chips need a little delay between SMBus communication. When
+ * set, the generic PMBus helper functions will wait if necessary
+ * to meet this requirement. The access delay is honored after
+ * every SMBus operation. The write delay is only honored after
+ * SMBus write operations.
+ */
+ int access_delay; /* in microseconds */
+ int write_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) \
@@ -485,6 +503,7 @@ 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)
@@ -500,6 +519,7 @@ 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_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0)
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index cb4c65a7f288..787683e83db6 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -7,6 +7,7 @@
*/
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
@@ -30,6 +31,9 @@
#define PMBUS_ATTR_ALLOC_SIZE 32
#define PMBUS_NAME_SIZE 24
+static int wp = -1;
+module_param(wp, int, 0444);
+
struct pmbus_sensor {
struct pmbus_sensor *next;
char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */
@@ -85,6 +89,8 @@ struct pmbus_data {
u32 flags; /* from platform data */
+ u8 revision; /* The PMBus revision the device is compliant with */
+
int exponent[PMBUS_PAGES];
/* linear mode: exponent for output voltages */
@@ -108,6 +114,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 */
};
struct pmbus_debugfs_entry {
@@ -145,7 +153,7 @@ void pmbus_clear_cache(struct i2c_client *client)
for (sensor = data->sensors; sensor; sensor = sensor->next)
sensor->data = -ENODATA;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, "PMBUS");
void pmbus_set_update(struct i2c_client *client, u8 reg, bool update)
{
@@ -156,7 +164,40 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update)
if (sensor->reg == reg)
sensor->update = update;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS");
+
+/* Some chips need a delay between accesses. */
+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);
+
+ if (delta < info->write_delay)
+ fsleep(info->write_delay - delta);
+ }
+}
+
+/* Sets the last accessed timestamp for pmbus_wait */
+static void pmbus_update_ts(struct i2c_client *client, bool write_op)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+
+ if (info->access_delay) {
+ data->access_time = ktime_get();
+ } else if (info->write_delay && write_op) {
+ data->write_time = ktime_get();
+ }
+}
int pmbus_set_page(struct i2c_client *client, int page, int phase)
{
@@ -168,11 +209,15 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) &&
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);
if (rv < 0)
return rv;
+ pmbus_wait(client);
rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
+ pmbus_update_ts(client, false);
if (rv < 0)
return rv;
@@ -183,8 +228,10 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
if (data->info->phases[page] && data->currphase != phase &&
!(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) {
+ pmbus_wait(client);
rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
phase);
+ pmbus_update_ts(client, true);
if (rv)
return rv;
}
@@ -192,7 +239,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
return 0;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_set_page, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_set_page, "PMBUS");
int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
{
@@ -202,9 +249,13 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
if (rv < 0)
return rv;
- return i2c_smbus_write_byte(client, value);
+ pmbus_wait(client);
+ rv = i2c_smbus_write_byte(client, value);
+ pmbus_update_ts(client, true);
+
+ return rv;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, "PMBUS");
/*
* _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if
@@ -233,9 +284,13 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
if (rv < 0)
return rv;
- return i2c_smbus_write_word_data(client, reg, word);
+ pmbus_wait(client);
+ rv = i2c_smbus_write_word_data(client, reg, word);
+ pmbus_update_ts(client, true);
+
+ return rv;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, "PMBUS");
static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
@@ -341,7 +396,7 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id,
return _pmbus_write_word_data(client, page,
pmbus_fan_command_registers[id], command);
}
-EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, "PMBUS");
int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
{
@@ -351,9 +406,13 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
if (rv < 0)
return rv;
- return i2c_smbus_read_word_data(client, reg);
+ pmbus_wait(client);
+ rv = i2c_smbus_read_word_data(client, reg);
+ pmbus_update_ts(client, false);
+
+ return rv;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, "PMBUS");
static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg)
{
@@ -410,9 +469,13 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
if (rv < 0)
return rv;
- return i2c_smbus_read_byte_data(client, reg);
+ pmbus_wait(client);
+ rv = i2c_smbus_read_byte_data(client, reg);
+ pmbus_update_ts(client, false);
+
+ return rv;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, "PMBUS");
int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
{
@@ -422,9 +485,13 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
if (rv < 0)
return rv;
- return i2c_smbus_write_byte_data(client, reg, value);
+ pmbus_wait(client);
+ rv = i2c_smbus_write_byte_data(client, reg, value);
+ pmbus_update_ts(client, true);
+
+ return rv;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, "PMBUS");
int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
u8 mask, u8 value)
@@ -443,7 +510,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
return rv;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS");
static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
char *data_buf)
@@ -454,7 +521,11 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
if (rv < 0)
return rv;
- return i2c_smbus_read_block_data(client, reg, data_buf);
+ pmbus_wait(client);
+ rv = i2c_smbus_read_block_data(client, reg, data_buf);
+ pmbus_update_ts(client, false);
+
+ return rv;
}
static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
@@ -510,14 +581,14 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id,
{
return pmbus_get_fan_rate(client, page, id, mode, false);
}
-EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, "PMBUS");
int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id,
enum pmbus_fan_mode mode)
{
return pmbus_get_fan_rate(client, page, id, mode, true);
}
-EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, "PMBUS");
static void pmbus_clear_fault_page(struct i2c_client *client, int page)
{
@@ -532,7 +603,7 @@ void pmbus_clear_faults(struct i2c_client *client)
for (i = 0; i < data->info->pages; i++)
pmbus_clear_fault_page(client, i);
}
-EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, "PMBUS");
static int pmbus_check_status_cml(struct i2c_client *client)
{
@@ -587,13 +658,13 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg)
{
return pmbus_check_register(client, _pmbus_read_byte_data, page, reg);
}
-EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, "PMBUS");
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg)
{
return pmbus_check_register(client, __pmbus_read_word_data, page, reg);
}
-EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, "PMBUS");
static bool __maybe_unused pmbus_check_block_register(struct i2c_client *client,
int page, int reg)
@@ -617,7 +688,7 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
return data->info;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, "PMBUS");
static int pmbus_get_status(struct i2c_client *client, int page, int reg)
{
@@ -1095,9 +1166,14 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
regval = status & mask;
if (regval) {
- ret = _pmbus_write_byte_data(client, page, reg, regval);
- if (ret)
- goto unlock;
+ if (data->revision >= PMBUS_REV_12) {
+ ret = _pmbus_write_byte_data(client, page, reg, regval);
+ if (ret)
+ goto unlock;
+ } else {
+ pmbus_clear_fault_page(client, page);
+ }
+
}
if (s1 && s2) {
s64 v1, v2;
@@ -2450,9 +2526,11 @@ static int pmbus_read_coefficients(struct i2c_client *client,
data.block[1] = attr->reg;
data.block[2] = 0x01;
+ pmbus_wait(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);
if (rv < 0)
return rv;
@@ -2590,6 +2668,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)
{
@@ -2604,7 +2732,10 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
/* Enable PEC if the controller and bus supports it */
if (!(data->flags & PMBUS_NO_CAPABILITY)) {
+ pmbus_wait(client);
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
+ pmbus_update_ts(client, false);
+
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC))
client->flags |= I2C_CLIENT_PEC;
@@ -2617,10 +2748,16 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
* Bail out if both registers are not supported.
*/
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);
+
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);
+
if (ret < 0 || ret == 0xff) {
dev_err(dev, "PMBus status register not found\n");
return -ENODEV;
@@ -2634,11 +2771,12 @@ 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 = i2c_smbus_read_byte_data(client, 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)
+ data->revision = ret;
if (data->info->pages)
pmbus_clear_faults(client);
@@ -3094,8 +3232,12 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
{
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;
@@ -3128,7 +3270,23 @@ const struct regulator_ops pmbus_regulator_ops = {
.set_voltage = pmbus_regulator_set_voltage,
.list_voltage = pmbus_regulator_list_voltage,
};
-EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
+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)
{
@@ -3188,7 +3346,17 @@ static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, u8 val)
{
- return _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
+ int ret;
+
+ ret = _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
+
+ /*
+ * Clear fault systematically in case writing PMBUS_SMBALERT_MASK
+ * is not supported by the chip.
+ */
+ pmbus_clear_fault_page(client, page);
+
+ return ret;
}
static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
@@ -3366,11 +3534,11 @@ static int pmbus_init_debugfs(struct i2c_client *client,
/*
* Allocate the max possible entries we need.
- * 6 entries device-specific
+ * 7 entries device-specific
* 10 entries page-specific
*/
entries = devm_kcalloc(data->dev,
- 6 + data->info->pages * 10, sizeof(*entries),
+ 7 + data->info->pages * 10, sizeof(*entries),
GFP_KERNEL);
if (!entries)
return -ENOMEM;
@@ -3383,6 +3551,15 @@ 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_byte_register(client, 0, PMBUS_REVISION)) {
+ entries[idx].client = client;
+ entries[idx].page = 0;
+ entries[idx].reg = PMBUS_REVISION;
+ debugfs_create_file("revision", 0444, data->debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
+
if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) {
entries[idx].client = client;
entries[idx].page = 0;
@@ -3644,7 +3821,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
return 0;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, "PMBUS");
struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
{
@@ -3652,7 +3829,7 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
return data->debugfs;
}
-EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, "PMBUS");
int pmbus_lock_interruptible(struct i2c_client *client)
{
@@ -3660,7 +3837,7 @@ int pmbus_lock_interruptible(struct i2c_client *client)
return mutex_lock_interruptible(&data->update_lock);
}
-EXPORT_SYMBOL_NS_GPL(pmbus_lock_interruptible, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_lock_interruptible, "PMBUS");
void pmbus_unlock(struct i2c_client *client)
{
@@ -3668,7 +3845,7 @@ void pmbus_unlock(struct i2c_client *client)
mutex_unlock(&data->update_lock);
}
-EXPORT_SYMBOL_NS_GPL(pmbus_unlock, PMBUS);
+EXPORT_SYMBOL_NS_GPL(pmbus_unlock, "PMBUS");
static int __init pmbus_core_init(void)
{
diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c
index e2790a682dc8..6a4a978eca7e 100644
--- a/drivers/hwmon/pmbus/pxe1610.c
+++ b/drivers/hwmon/pmbus/pxe1610.c
@@ -127,9 +127,9 @@ static int pxe1610_probe(struct i2c_client *client)
}
static const struct i2c_device_id pxe1610_id[] = {
- {"pxe1610", 0},
- {"pxe1110", 0},
- {"pxm1310", 0},
+ {"pxe1610"},
+ {"pxe1110"},
+ {"pxm1310"},
{}
};
@@ -148,4 +148,4 @@ module_i2c_driver(pxe1610_driver);
MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>");
MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c
index a235c1cdf4fe..4d7086d83aa3 100644
--- a/drivers/hwmon/pmbus/q54sj108a2.c
+++ b/drivers/hwmon/pmbus/q54sj108a2.c
@@ -421,4 +421,4 @@ module_i2c_driver(q54sj108a2_driver);
MODULE_AUTHOR("Xiao.Ma <xiao.mx.ma@deltaww.com>");
MODULE_DESCRIPTION("PMBus driver for Delta Q54SJ108A2 series modules");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/stpddc60.c b/drivers/hwmon/pmbus/stpddc60.c
index be9076dcc2db..5cb905ed8ae5 100644
--- a/drivers/hwmon/pmbus/stpddc60.c
+++ b/drivers/hwmon/pmbus/stpddc60.c
@@ -18,8 +18,8 @@
#define STPDDC60_MFR_UV_LIMIT_OFFSET 0xe6
static const struct i2c_device_id stpddc60_id[] = {
- {"stpddc60", 0},
- {"bmr481", 0},
+ {"stpddc60"},
+ {"bmr481"},
{}
};
MODULE_DEVICE_TABLE(i2c, stpddc60_id);
@@ -246,4 +246,4 @@ module_i2c_driver(stpddc60_driver);
MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
MODULE_DESCRIPTION("PMBus driver for ST STPDDC60");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c
index c31889a036f0..07fe58c24485 100644
--- a/drivers/hwmon/pmbus/tda38640.c
+++ b/drivers/hwmon/pmbus/tda38640.c
@@ -195,7 +195,7 @@ static int tda38640_probe(struct i2c_client *client)
}
static const struct i2c_device_id tda38640_id[] = {
- {"tda38640", 0},
+ {"tda38640"},
{}
};
MODULE_DEVICE_TABLE(i2c, tda38640_id);
@@ -221,4 +221,4 @@ module_i2c_driver(tda38640_driver);
MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>");
MODULE_DESCRIPTION("PMBus driver for Infineon TDA38640");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c
new file mode 100644
index 000000000000..0d2655e69549
--- /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("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/tps40422.c b/drivers/hwmon/pmbus/tps40422.c
index ea0074a6b9fc..7c9fedaa068c 100644
--- a/drivers/hwmon/pmbus/tps40422.c
+++ b/drivers/hwmon/pmbus/tps40422.c
@@ -31,7 +31,7 @@ static int tps40422_probe(struct i2c_client *client)
}
static const struct i2c_device_id tps40422_id[] = {
- {"tps40422", 0},
+ {"tps40422"},
{}
};
@@ -51,4 +51,4 @@ module_i2c_driver(tps40422_driver);
MODULE_AUTHOR("Zhu Laiwen <richard.zhu@nsn.com>");
MODULE_DESCRIPTION("PMBus driver for TI TPS40422");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c
index 5c9466244d70..63524dff5e75 100644
--- a/drivers/hwmon/pmbus/tps53679.c
+++ b/drivers/hwmon/pmbus/tps53679.c
@@ -308,4 +308,4 @@ module_i2c_driver(tps53679_driver);
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/tps546d24.c b/drivers/hwmon/pmbus/tps546d24.c
index 69bbdb6c680b..44d7a6df1dbd 100644
--- a/drivers/hwmon/pmbus/tps546d24.c
+++ b/drivers/hwmon/pmbus/tps546d24.c
@@ -42,7 +42,7 @@ static int tps546d24_probe(struct i2c_client *client)
}
static const struct i2c_device_id tps546d24_id[] = {
- {"tps546d24", 0},
+ {"tps546d24"},
{}
};
MODULE_DEVICE_TABLE(i2c, tps546d24_id);
@@ -68,4 +68,4 @@ module_i2c_driver(tps546d24_driver);
MODULE_AUTHOR("Duke Du <dukedu83@gmail.com>");
MODULE_DESCRIPTION("PMBus driver for TI tps546d24");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index 8d9d422450e5..9b0eadc81a2e 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -67,7 +67,6 @@ struct ucd9000_data {
struct gpio_chip gpio;
#endif
struct dentry *debugfs;
- ktime_t write_time;
};
#define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
@@ -80,68 +79,11 @@ struct ucd9000_debugfs_entry {
* It has been observed that the UCD90320 randomly fails register access when
* doing another access right on the back of a register write. To mitigate this
* make sure that there is a minimum delay between a write access and the
- * following access. The 250us is based on experimental data. At a delay of
- * 200us the issue seems to go away. Add a bit of extra margin to allow for
+ * following access. The 500 is based on experimental data. At a delay of
+ * 350us the issue seems to go away. Add a bit of extra margin to allow for
* system to system differences.
*/
-#define UCD90320_WAIT_DELAY_US 250
-
-static inline void ucd90320_wait(const struct ucd9000_data *data)
-{
- s64 delta = ktime_us_delta(ktime_get(), data->write_time);
-
- if (delta < UCD90320_WAIT_DELAY_US)
- udelay(UCD90320_WAIT_DELAY_US - delta);
-}
-
-static int ucd90320_read_word_data(struct i2c_client *client, int page,
- int phase, int reg)
-{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct ucd9000_data *data = to_ucd9000_data(info);
-
- if (reg >= PMBUS_VIRT_BASE)
- return -ENXIO;
-
- ucd90320_wait(data);
- return pmbus_read_word_data(client, page, phase, reg);
-}
-
-static int ucd90320_read_byte_data(struct i2c_client *client, int page, int reg)
-{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct ucd9000_data *data = to_ucd9000_data(info);
-
- ucd90320_wait(data);
- return pmbus_read_byte_data(client, page, reg);
-}
-
-static int ucd90320_write_word_data(struct i2c_client *client, int page,
- int reg, u16 word)
-{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct ucd9000_data *data = to_ucd9000_data(info);
- int ret;
-
- ucd90320_wait(data);
- ret = pmbus_write_word_data(client, page, reg, word);
- data->write_time = ktime_get();
-
- return ret;
-}
-
-static int ucd90320_write_byte(struct i2c_client *client, int page, u8 value)
-{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct ucd9000_data *data = to_ucd9000_data(info);
- int ret;
-
- ucd90320_wait(data);
- ret = pmbus_write_byte(client, page, value);
- data->write_time = ktime_get();
-
- return ret;
-}
+#define UCD90320_WAIT_DELAY_US 500
static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
{
@@ -667,10 +609,8 @@ static int ucd9000_probe(struct i2c_client *client)
info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12
| PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34;
} else if (mid->driver_data == ucd90320) {
- info->read_byte_data = ucd90320_read_byte_data;
- info->read_word_data = ucd90320_read_word_data;
- info->write_byte = ucd90320_write_byte;
- info->write_word_data = ucd90320_write_word_data;
+ /* Delay SMBus operations after a write */
+ info->write_delay = UCD90320_WAIT_DELAY_US;
}
ucd9000_probe_gpio(client, mid, data);
@@ -702,4 +642,4 @@ module_i2c_driver(ucd9000_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c
index 7920d1c06df0..f68adaf4a110 100644
--- a/drivers/hwmon/pmbus/ucd9200.c
+++ b/drivers/hwmon/pmbus/ucd9200.c
@@ -209,4 +209,4 @@ module_i2c_driver(ucd9200_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/xdp710.c b/drivers/hwmon/pmbus/xdp710.c
new file mode 100644
index 000000000000..660bbfe16e1e
--- /dev/null
+++ b/drivers/hwmon/pmbus/xdp710.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Infineon XDP710 Hot-Swap Controller
+ */
+
+#include <linux/bitops.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include "pmbus.h"
+
+#define XDP710_REG_CFG 0xD3
+#define XDP710_V_SNS_CFG 0xD4
+#define XDP710_CS_RNG 0xD5
+
+/*
+ * The table to map configuration register values
+ * to sense resistor values
+ */
+static const int micro_ohm_rsense[] = {
+ 200, 250, 300, 330, 400, 470, 500, 600,
+ 670, 700, 750, 800, 900, 1000, 1100, 1200,
+ 1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900,
+ 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700,
+ 2800, 3000, 3100, 3200, 3300, 3400, 3500, 3600,
+ 3700, 3800, 3900, 4000, 4100, 4200, 4300, 4400,
+ 4500, 4600, 4700, 4800, 4900, 5000, 5500, 6000,
+ 6500, 7000, 7500, 8000, 8500, 9000, 9500, 10000
+};
+
+static struct pmbus_driver_info xdp710_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_POWER] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_VOLTAGE_IN] = 4653,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = -2,
+ .m[PSC_VOLTAGE_OUT] = 4653,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = -2,
+ .m[PSC_CURRENT_OUT] = 23165,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = -2,
+ .m[PSC_POWER] = 4211,
+ .b[PSC_POWER] = 0,
+ .R[PSC_POWER] = -2,
+ .m[PSC_TEMPERATURE] = 52,
+ .b[PSC_TEMPERATURE] = 14321,
+ .R[PSC_TEMPERATURE] = -1,
+ .func[0] =
+ PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
+};
+
+static int xdp710_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+ u8 cs_rng;
+ u8 vtlm_rng;
+ int rsense;
+ int ret;
+ int m = 0;
+
+ info = devm_kmemdup(&client->dev, &xdp710_info, sizeof(*info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ ret = i2c_smbus_read_word_data(client, XDP710_CS_RNG);
+ if (ret < 0) {
+ dev_err(&client->dev, "Can't get CS_RNG");
+ return ret;
+ }
+ cs_rng = (ret >> 6) & GENMASK(1, 0);
+
+ ret = i2c_smbus_read_word_data(client, XDP710_V_SNS_CFG);
+ if (ret < 0) {
+ dev_err(&client->dev, "Can't get V_SNS_CFG");
+ return ret;
+ }
+ vtlm_rng = ret & GENMASK(1, 0);
+
+ ret = i2c_smbus_read_word_data(client, XDP710_REG_CFG);
+ if (ret < 0) {
+ dev_err(&client->dev, "Can't get REG_CFG");
+ return ret;
+ }
+ ret &= GENMASK(5, 0);
+ rsense = micro_ohm_rsense[ret];
+
+ info->m[PSC_VOLTAGE_IN] <<= vtlm_rng;
+ info->m[PSC_VOLTAGE_OUT] <<= vtlm_rng;
+
+ m = info->m[PSC_CURRENT_OUT];
+ info->m[PSC_CURRENT_OUT] = DIV_ROUND_CLOSEST(m * rsense >> cs_rng, 1000);
+
+ m = info->m[PSC_POWER];
+ info->m[PSC_POWER] = DIV_ROUND_CLOSEST(m * rsense >> cs_rng, 1000);
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct of_device_id xdp710_of_match[] = {
+ { .compatible = "infineon,xdp710" },
+ {}
+};
+
+static const struct i2c_device_id xdp710_id[] = {
+ {"xdp710"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, xdp710_id);
+
+static struct i2c_driver xdp710_driver = {
+ .driver = {
+ .name = "xdp710",
+ .of_match_table = xdp710_of_match,
+ },
+ .probe = xdp710_probe,
+ .id_table = xdp710_id,
+};
+module_i2c_driver(xdp710_driver);
+
+MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>");
+MODULE_DESCRIPTION("PMBus driver for XDP710 HSC");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c
index 37d08dd427d5..f3aa6339d60d 100644
--- a/drivers/hwmon/pmbus/xdpe12284.c
+++ b/drivers/hwmon/pmbus/xdpe12284.c
@@ -164,9 +164,9 @@ static int xdpe122_probe(struct i2c_client *client)
}
static const struct i2c_device_id xdpe122_id[] = {
- {"xdpe11280", 0},
- {"xdpe12254", 0},
- {"xdpe12284", 0},
+ {"xdpe11280"},
+ {"xdpe12254"},
+ {"xdpe12284"},
{}
};
@@ -194,4 +194,4 @@ module_i2c_driver(xdpe122_driver);
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/xdpe152c4.c b/drivers/hwmon/pmbus/xdpe152c4.c
index 235e6b41ae4c..67a3d5fe1daf 100644
--- a/drivers/hwmon/pmbus/xdpe152c4.c
+++ b/drivers/hwmon/pmbus/xdpe152c4.c
@@ -44,8 +44,8 @@ static int xdpe152_probe(struct i2c_client *client)
}
static const struct i2c_device_id xdpe152_id[] = {
- {"xdpe152c4", 0},
- {"xdpe15284", 0},
+ {"xdpe152c4"},
+ {"xdpe15284"},
{}
};
@@ -72,4 +72,4 @@ module_i2c_driver(xdpe152_driver);
MODULE_AUTHOR("Greg Schwendimann <greg.schwendimann@infineon.com>");
MODULE_DESCRIPTION("PMBus driver for Infineon XDPE152 family");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c
index 83458df0d0cf..97be69630cfb 100644
--- a/drivers/hwmon/pmbus/zl6100.c
+++ b/drivers/hwmon/pmbus/zl6100.c
@@ -22,8 +22,6 @@ enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105,
struct zl6100_data {
int id;
- ktime_t access; /* chip access time */
- int delay; /* Delay between chip accesses in uS */
struct pmbus_driver_info info;
};
@@ -122,16 +120,6 @@ static u16 zl6100_d2l(long val)
return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
}
-/* Some chips need a delay between accesses */
-static inline void zl6100_wait(const struct zl6100_data *data)
-{
- if (data->delay) {
- s64 delta = ktime_us_delta(ktime_get(), data->access);
- if (delta < data->delay)
- udelay(data->delay - delta);
- }
-}
-
static int zl6100_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
@@ -174,9 +162,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page,
break;
}
- zl6100_wait(data);
ret = pmbus_read_word_data(client, page, phase, vreg);
- data->access = ktime_get();
if (ret < 0)
return ret;
@@ -195,14 +181,11 @@ static int zl6100_read_word_data(struct i2c_client *client, int page,
static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct zl6100_data *data = to_zl6100_data(info);
int ret, status;
if (page >= info->pages)
return -ENXIO;
- zl6100_wait(data);
-
switch (reg) {
case PMBUS_VIRT_STATUS_VMON:
ret = pmbus_read_byte_data(client, 0,
@@ -225,7 +208,6 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
ret = pmbus_read_byte_data(client, page, reg);
break;
}
- data->access = ktime_get();
return ret;
}
@@ -234,8 +216,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
u16 word)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct zl6100_data *data = to_zl6100_data(info);
- int ret, vreg;
+ int vreg;
if (page >= info->pages)
return -ENXIO;
@@ -265,27 +246,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
vreg = reg;
}
- zl6100_wait(data);
- ret = pmbus_write_word_data(client, page, vreg, word);
- data->access = ktime_get();
-
- return ret;
-}
-
-static int zl6100_write_byte(struct i2c_client *client, int page, u8 value)
-{
- const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
- struct zl6100_data *data = to_zl6100_data(info);
- int ret;
-
- if (page >= info->pages)
- return -ENXIO;
-
- zl6100_wait(data);
- ret = pmbus_write_byte(client, page, value);
- data->access = ktime_get();
-
- return ret;
+ return pmbus_write_word_data(client, page, vreg, word);
}
static const struct i2c_device_id zl6100_id[] = {
@@ -363,14 +324,7 @@ static int zl6100_probe(struct i2c_client *client)
* supported chips are known to require a wait time between I2C
* accesses.
*/
- data->delay = delay;
-
- /*
- * Since there was a direct I2C device access above, wait before
- * accessing the chip again.
- */
- data->access = ktime_get();
- zl6100_wait(data);
+ udelay(delay);
info = &data->info;
@@ -404,8 +358,7 @@ static int zl6100_probe(struct i2c_client *client)
if (ret < 0)
return ret;
- data->access = ktime_get();
- zl6100_wait(data);
+ udelay(delay);
if (ret & ZL8802_MFR_PHASES_MASK)
info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
@@ -418,8 +371,7 @@ static int zl6100_probe(struct i2c_client *client)
if (ret < 0)
return ret;
- data->access = ktime_get();
- zl6100_wait(data);
+ udelay(delay);
ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG);
if (ret < 0)
@@ -428,8 +380,7 @@ static int zl6100_probe(struct i2c_client *client)
if (ret & ZL8802_MFR_XTEMP_ENABLE_2)
info->func[i] |= PMBUS_HAVE_TEMP2;
- data->access = ktime_get();
- zl6100_wait(data);
+ udelay(delay);
}
ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG);
if (ret < 0)
@@ -446,13 +397,12 @@ static int zl6100_probe(struct i2c_client *client)
info->func[0] |= PMBUS_HAVE_TEMP2;
}
- data->access = ktime_get();
- zl6100_wait(data);
+ udelay(delay);
+ info->access_delay = delay;
info->read_word_data = zl6100_read_word_data;
info->read_byte_data = zl6100_read_byte_data;
info->write_word_data = zl6100_write_word_data;
- info->write_byte = zl6100_write_byte;
return pmbus_do_probe(client, info);
}
@@ -470,4 +420,4 @@ module_i2c_driver(zl6100_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(PMBUS);
+MODULE_IMPORT_NS("PMBUS");